connect-four/server.js

476 lines
11 KiB
JavaScript

const express = require('express')
const socketio = require('socket.io')
const http = require('http')
const path = require('path')
let app = express()
let server = http.createServer(app)
let io = socketio(server)
app.enable('trust proxy')
app.disable('x-powered-by')
app.use('/', express.static(path.join(__dirname, '/client/')))
function playerNameValidation (name) {
if (/^([A-Z0-9_\-@]{3,20})$/i.test(name)) {
return true
}
return false
}
let clients = {}
let games = {}
let totalGames = 0
// Generate a random int betweem two ints
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
// Generate random string of characters
function nuid(len) {
let buf = [],
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
charlen = chars.length
for (let i = 0; i < len; ++i) {
buf.push(chars[getRandomInt(0, charlen - 1)])
}
return buf.join('')
}
function clientsBySocketID (id) {
let result = null
for (let uid in clients) {
let client = clients[uid]
if (client.sockID === id) {
result = uid
}
}
return result
}
function determineOpponent (myIndex) {
let opponent = 'red'
if (myIndex === 'red') {
opponent = 'blue'
}
return opponent
}
function killGamesClientIsIn (uid) {
for (let gameId in games) {
let game = games[gameId]
if (game.blue && game.blue === uid) {
if (!game.isWaiting && game.red) {
clients[game.red].socket.emit('game_end', {win: true, result: 0})
}
} else if (game.red && game.red === uid) {
if (clients[game.blue]) {
clients[game.blue].socket.emit('game_end', {win: true, result: 0})
}
} else {
continue
}
delete games[gameId]
console.log(gameId + ' was ended abruptly on ' + uid + '\'s demand.')
}
}
function createNewGame (uid) {
let client = clients[uid]
let gameId = nuid(16)
client.socket.emit('game_new_done', {gameId: gameId})
console.log(client.name + ' has started a new game. ID: ' + gameId)
games[gameId] = {
blue: uid,
red: null,
isWaiting: true,
turn: 1,
places: [[],[],[],[],[],[],[],[],[]],
created: new Date(),
started: null
}
}
function joinGame (uid, gameId) {
let me = clients[uid]
if (!games[gameId]) {
return me.socket.emit('game_error', {message: 'That game has ended!'})
}
if (games[gameId].red != null) {
return me.socket.emit('game_error', {message: 'That game has already started!'})
}
if (!clients[games[gameId].blue]) {
return me.socket.emit('game_error', {message: 'That game has ended!'})
}
let game = games[gameId]
game.red = uid
game.isWaiting = false
game.started = new Date()
let opponent = clients[game.blue]
if (!opponent) {
return me.socket.emit('game_error', {message: 'Your opponent abruptly dissappeared, what?'})
}
opponent.socket.emit('game_start', {gameId: gameId, opponentId: uid, opponentName: me.name, color: 'blue'})
me.socket.emit('game_start', {gameId: gameId, opponentId: opponent.uid, opponentName: opponent.name, color: 'red'})
game.turn = 'blue'
clients[game.blue].socket.emit('turn', true)
clients[uid].socket.emit('turn', false)
totalGames += 1
}
function endGame (gameId, victoryId, loserId, status) {
if (clients[victoryId]) {
clients[victoryId].socket.emit('game_end', {win: true, result: status})
}
if (clients[loserId]) {
clients[loserId].socket.emit('game_end', {win: false, result: status})
}
delete games[gameId]
console.log(gameId + ' ended with ' + victoryId + '\'s victory.')
}
function waitingGamesList (uid) {
let result = []
let cap = 0
let gamesInSession = 0
for (let i in games) {
let game = games[i]
if (!game.isWaiting) {
gamesInSession += 1
}
}
for (let gameId in games) {
if (cap >= 20) break
let game = games[gameId]
if (game.isWaiting) {
let userName = clients[game.blue].name
if (uid && game.blue === uid) continue
result.push({
gameId: gameId,
name: userName,
started: game.started
})
cap += 1
}
}
return {
online: Object.keys(clients).length,
sessions: gamesInSession,
totalGames: totalGames,
list: result
}
}
function determinePlayerById (gameId, uid) {
let game = games[gameId]
if (!game) return null
if (game.blue && game.blue === uid) {
return 'blue'
} else if (game.red && game.red === uid) {
return 'red'
}
return null
}
function getPiece (game, col, index) {
if (col > 8 || index > 8) return
let colmn = game.places[col]
if (!colmn) return
if (!colmn.length) {
return
}
let match = null
for (let i in colmn) {
if (colmn[i].y === index) {
match = colmn[i]
match.col = col
break
}
}
return match
}
function detectWin (color, game) {
let win = false
let matches = []
for (let c in game.places) {
let col = game.places[c]
if (win) break
for (let p in col) {
let piece = col[p]
matches = []
for (let i = 0; i < 4; i++) {
let pAt = getPiece(game, parseInt(c) + i, piece.y)
if (pAt && pAt.color === color) {
matches.push(pAt)
} else {
matches = []
}
}
if (matches.length >= 4) {
win = true
console.log('horizontal win')
break
}
matches = []
for (let i = 0; i < 4; i++) {
let pAt = getPiece(game, parseInt(c), piece.y + i)
if (pAt && pAt.color === color) {
matches.push(pAt)
} else {
matches = []
}
}
if (matches.length >= 4) {
console.log('vertical win')
win = true
break
}
matches = []
for (let i = 0; i < 4; i++) {
let pAt = getPiece(game, parseInt(c) + i, piece.y - i)
if (pAt && pAt.color === color) {
matches.push(pAt)
} else {
matches = []
}
}
if (matches.length >= 4) {
console.log('diagonal right win')
win = true
break
}
matches = []
for (let i = 0; i < 4; i++) {
let pAt = getPiece(game, parseInt(c) - i, piece.y - i)
if (pAt && pAt.color === color) {
matches.push(pAt)
} else {
matches = []
}
}
if (matches.length >= 4) {
console.log('diagonal left win')
win = true
break
}
}
}
if (win) {
return matches
}
return null
}
function detectTie (game) {
let tie = true
for (let c in game.places) {
let col = game.places[c]
if (col.length !== 9) {
tie = false
break
}
}
return tie
}
io.on('connection', (socket) => {
socket.on('session_create', (data) => {
if (!data.name) {
return socket.emit('login_status', {success: false, message: 'Invalid name.'})
}
if (!playerNameValidation(data.name)) {
return socket.emit('login_status', {success: false, message: 'Invalid name.'})
}
let playerUid = nuid(32)
socket.emit('login_status', {success: true, uid: playerUid, name: data.name})
clients[playerUid] = {
socket: socket,
name: data.name,
sockID: socket.conn.id
}
console.log('New player: "' + data.name + '" with uid ' + playerUid)
})
socket.on('poll_games', () => {
let client = clientsBySocketID(socket.conn.id)
socket.emit('poll_games_res', waitingGamesList(client))
})
socket.on('game_attempt_join', (data) => {
let client = clientsBySocketID(socket.conn.id)
if (!client) {
socket.emit('game_error', {message: 'You are not logged in properly!'})
socket.emit('force_relog')
return
}
if (!data.gameId) return
joinGame(client, data.gameId)
})
socket.on('leave_game', (data) => {
let client = clientsBySocketID(socket.conn.id)
if (!client) return
killGamesClientIsIn(client)
socket.emit('left_success')
})
socket.on('new_game', () => {
let client = clientsBySocketID(socket.conn.id)
if (!client) {
socket.emit('game_error', {message: 'You are not logged in properly!'})
socket.emit('force_relog')
return
}
createNewGame(client)
})
socket.on('chat_send', (data) => {
let client = clientsBySocketID(socket.conn.id)
if (!client) {
socket.emit('game_error', {message: 'You are not logged in properly!'})
socket.emit('force_relog')
return
}
if (!data.gameId) return
let game = games[data.gameId]
let playerInGame = determinePlayerById(data.gameId, client)
if (!playerInGame) {
socket.emit('game_error', {message: 'unexpected error. code: 763'})
return
}
let opponent = determineOpponent(playerInGame)
let opponentObj = game[opponent]
let me = game[playerInGame]
clients[opponentObj].socket.emit('chat', {name: clients[me].name, message: data.message})
})
socket.on('place_at', (data) => {
let client = clientsBySocketID(socket.conn.id)
if (!client) {
socket.emit('game_error', {message: 'You are not logged in properly!'})
socket.emit('force_relog')
return
}
if (!data.gameId) return
let game = games[data.gameId]
let playerInGame = determinePlayerById(data.gameId, client)
if (!playerInGame) {
socket.emit('game_error', {message: 'unexpected error. code: 763'})
return
}
if (data.column == null || data.column > 9) {
socket.emit('game_error', {message: 'Unexpected column'})
return
}
if (game.turn !== playerInGame) {
socket.emit('game_error', {message: 'It\'s not your turn!'})
return
}
let opponentColor = determineOpponent(playerInGame)
let opponent = game[opponentColor]
let me = game[playerInGame]
clients[me].socket.emit('place', {column: data.column, color: playerInGame})
clients[opponent].socket.emit('place', {column: data.column, color: playerInGame})
game.places[data.column].push({color: playerInGame, y: 8 - game.places[data.column].length})
game.turn = opponentColor
clients[me].socket.emit('turn', false)
clients[opponent].socket.emit('turn', true)
let win = detectWin(playerInGame, game)
if (win) {
clients[me].socket.emit('win_tiles', win)
clients[opponent].socket.emit('win_tiles', win)
endGame(data.gameId, me, opponent, 1)
return
}
if (detectTie(game)) {
endGame(data.gameId, me, opponent, 2)
return
}
})
socket.on('disconnect', () => {
let client = clientsBySocketID(socket.conn.id)
if (!client) return
killGamesClientIsIn(client)
console.log('Player uid ' + client + ' left.')
delete clients[client]
})
})
server.listen(8245)