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
|
const Http = require("http")
const WebSocket = require("ws")
const Express = require("express")
const { stat } = require("fs")
const Game = require("./game")
const Color = require("./public/javascripts/color")
const Messages = require("./public/javascripts/messages")
const Environment = require("./environment")
const indexRouter = require("./routes/index")
const stats = require("./statTracker")
/* Get the server port, if the user doesn't provide one then print an error to STDERR and exit */
const port = process.argv[2]
if (!port) {
process.stderr.write("Usage: npm start port")
process.exit(1)
}
/* Initialize the routes */
const app = Express()
app.set("view engine", "ejs")
app.use(Express.static(__dirname + "/public"))
app.get("/", indexRouter)
app.get("/play", indexRouter)
/* Initialize the server and websocket server */
const server = Http.createServer(app)
const wss = new WebSocket.Server({ server })
/* Initialize the environment */
let env = new Environment()
wss.on("connection", ws => {
/* When a new client connects, get the first game whos status is pending and add the client to
* that game. If there is no such game, then create a new one which will wait for a second client
* to join.
*/
let game = env.games.filter(g => !g.ongoing)[0]
if (!game) {
game = new Game()
env.games.push(game)
game.bluePlayer = ws
game.messageClient({ head: Messages.WELCOME, body: Color.BLUE }, ws)
} else {
game.redPlayer = ws
game.ongoing = true
game.messageClient({ head: Messages.WELCOME, body: Color.RED }, ws)
/* Once the red player joins the game, we can officially start this game and inform the blue
* player that he can make his opening move.
*/
game.messageOpponent({ head: Messages.START }, ws)
stats.ongoingGames++
stats.totalGames++
game.nextTurn()
}
/* When a client disconnects, check if the game they were part of was ongoing (the game could
* have been waiting for a second client to join). If it was, message the opponent that they
* have won. Regardless of the state of the game, make sure to remove the game from the games
* array.
*/
ws.on("close", () => {
if (game.ongoing && env.games.includes(game)) {
game.messageOpponent({ head: Messages.DISCONNECT }, ws)
stats.ongoingGames--
stats.totalGames--
}
env.removeGame(game)
})
ws.on("message", msg => {
msg = JSON.parse(msg)
switch (msg.head) {
/* When a player resigns we check to see if the game is still ongoing and if it is we
* decrement the `ongoingGames' and `totalGames' counters.
*/
case Messages.RESIGN:
if (game.ongoing && env.games.includes(game)) {
game.messageOpponent(msg, ws)
stats.ongoingGames--
stats.totalGames--
}
env.removeGame(game)
break
/* When the client makes a move, we tell the opponent about the move that was made so that
* they can display the moved pieces on their end. We also tell the game object about the
* move so that it can calculate legal moves for the next turn.
*/
case Messages.MOVED:
game.messageOpponent({
head: Messages.MOVED,
body: {
id: msg.body.id,
position: msg.body.new,
captures: msg.body.captures,
king: msg.body.king,
history: msg.body.history
}
}, ws)
game.move(msg.body)
/* If this returns false, it means that one of the players won the game. In this case we
* need to update statistics regarding the amount of moves it takes to win a game and
* such. We also need to remove the game.
*/
if (!game.nextTurn()) {
const totalMoves = game.history.length
/* Update minimum amount of moves in stat tracker */
stats.minimumMoves = Math.min(totalMoves, stats.minimumMoves)
/* Update average amount of moves in stat tracker */
stats.averageMoves = stats.averageMoves == Infinity
? totalMoves
: (stats.averageMoves * (stats.totalGames - 1) + totalMoves) / stats.totalGames
/* Remove ongoing game */
stats.ongoingGames--
env.removeGame(game)
}
break
}
})
})
server.listen(port)
|