aboutsummaryrefslogtreecommitdiff
path: root/draughts/app.js
blob: b27553472fdd8b0875b2790c631339059611f8bf (plain) (blame)
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)