465 lines
9.3 KiB
JavaScript
465 lines
9.3 KiB
JavaScript
// Imports
|
|
const http = require("http");
|
|
const url = require("url");
|
|
const fs = require("fs");
|
|
const util = require("util");
|
|
|
|
// Vars
|
|
const hostname = "192.168.0.128";
|
|
const port = 3000;
|
|
|
|
const STATE_LOBBY = 0;
|
|
const STATE_GAME = 1;
|
|
var state = STATE_LOBBY;
|
|
|
|
var userSockets = [];
|
|
var userNames = [];
|
|
var userOK = [];
|
|
|
|
const BOX_TYPE_TIP = 0;
|
|
const BOX_TYPE_KEY = 1;
|
|
const BOX_TYPE_BOMB = 2;
|
|
var boxes = []; // 2D array with box objects
|
|
var currentUserID = -1;
|
|
var guardID = 0;
|
|
|
|
// Create HTTP server
|
|
const server = http.createServer((req, res) => {
|
|
// Parse url
|
|
const requrl = url.parse(req.url);
|
|
var reqpath = requrl.pathname;
|
|
if(reqpath == "/") reqpath = "/index.html";
|
|
|
|
// Respond to requests
|
|
try{
|
|
// Return requested page
|
|
res.statusCode = 200;
|
|
res.write(fs.readFileSync("html"+reqpath));
|
|
res.end();
|
|
|
|
}catch(error){
|
|
// 404 if page not found
|
|
res.statusCode = 404;
|
|
res.setHeader("Content-Type", "text/plain");
|
|
res.end("404 - Page Not Found");
|
|
}
|
|
});
|
|
|
|
|
|
// Create socket.io server
|
|
const io = require("socket.io")(server); // Can only be done when server is created
|
|
|
|
io.on("connection", (socket) => {
|
|
console.log("New user connected!");
|
|
|
|
// Init socket listeners
|
|
|
|
// Disconnect
|
|
socket.on("disconnect", () => {
|
|
console.log("User disconnected!");
|
|
|
|
// Remove from user lists
|
|
if(userSockets.indexOf(socket) != -1){
|
|
// When in users list
|
|
name = userNames[userSockets.indexOf(socket)];
|
|
|
|
userNames.splice(userSockets.indexOf(socket), 1);
|
|
userSockets.splice(userSockets.indexOf(socket), 1);
|
|
|
|
if(state == STATE_LOBBY){
|
|
// Send new lobby
|
|
userSockets.forEach(function(s){s.emit("LOBBY", userNames);});
|
|
}else{
|
|
// Send disconnect message
|
|
userSockets.forEach(function(s){s.emit("DISCONNECT", name);});
|
|
}
|
|
}
|
|
});
|
|
|
|
// JOIN
|
|
socket.on("JOIN", (username) => {
|
|
console.log("JOIN request for "+username);
|
|
|
|
// Check if can enter in lobby
|
|
if(state == STATE_LOBBY){
|
|
// Add user to lists
|
|
userSockets.push(socket);
|
|
userNames.push(username);
|
|
console.log(userNames);
|
|
|
|
// Send lobby data
|
|
userSockets.forEach(function(s){s.emit("LOBBY", userNames);});
|
|
|
|
}else{
|
|
// When can't enter
|
|
socket.emit("LOBBY_CLOSED");
|
|
}
|
|
});
|
|
|
|
// START
|
|
socket.on("START", () => {
|
|
console.log("START request");
|
|
|
|
// Send LAODING to all users
|
|
userSockets.forEach(function(s){s.emit("LOADING");});
|
|
|
|
// Set state
|
|
state = STATE_GAME;
|
|
|
|
// Create game
|
|
boxes = [];
|
|
var usedCodes = [];
|
|
for(y=0; y<6; y++){
|
|
boxes[y] = [];
|
|
for(x=0; x<6; x++){
|
|
var code;
|
|
do{
|
|
code = rand(10, 100);
|
|
}while(usedCodes.indexOf(code) != -1);
|
|
usedCodes.push(code);
|
|
|
|
boxes[y][x] = {type:BOX_TYPE_TIP, code:code, tip:""};
|
|
}
|
|
}
|
|
|
|
// Pick random boxes for key
|
|
var keyX = rand(0, 6);
|
|
var keyY = rand(0, 6);
|
|
var keyCode = boxes[keyY][keyX].code;
|
|
boxes[keyY][keyX].type = BOX_TYPE_KEY;
|
|
boxes[keyY][keyX].tip = "This is the key";
|
|
|
|
// Pick random boxes for bombs
|
|
var nrBombs = 4;
|
|
var bombXs = [];
|
|
var bombYs = [];
|
|
for(i=0; i<nrBombs; i++){
|
|
var bombX;
|
|
var bombY;
|
|
do{
|
|
bombX = rand(0, 6);
|
|
bombY = rand(0, 6);
|
|
}while(boxes[bombY][bombX].type != BOX_TYPE_TIP);
|
|
|
|
// Set to bomb
|
|
boxes[bombY][bombX].type = BOX_TYPE_BOMB;
|
|
boxes[bombY][bombX].tip = "This is a bomb";
|
|
|
|
// Add to list
|
|
bombXs.push(bombX);
|
|
bombYs.push(bombY);
|
|
}
|
|
|
|
// Pick guard
|
|
guardID = rand(0, userSockets.length);
|
|
console.log("GuardID = "+guardID+" ("+userNames[guardID]+")");
|
|
|
|
// Generate all possible tips
|
|
var tips = [];
|
|
|
|
// Bombs located at
|
|
for(i=0; i<nrBombs; i++){
|
|
tips.push(boxes[bombYs[i]][bombXs[i]].code+" is a bomb");
|
|
}
|
|
|
|
// x bombs in column
|
|
for(x=0; x<6; x++){
|
|
var n = 0;
|
|
for(y=0; y<6; y++){
|
|
if(boxes[y][x].type == BOX_TYPE_BOMB) n++;
|
|
}
|
|
var r = rand(0, 6);
|
|
|
|
tips.push("There "+(n==1?"is":"are")+" "+n+" bomb"+(n==1?"":"s")+" in the same column as "+boxes[r][x].code);
|
|
}
|
|
|
|
// x bombs in row
|
|
for(y=0; y<6; y++){
|
|
var n = 0;
|
|
for(x=0; x<6; x++){
|
|
if(boxes[y][x].type == BOX_TYPE_BOMB) n++;
|
|
}
|
|
var c = rand(0, 6);
|
|
|
|
tips.push("There "+(n==1?"is":"are")+" "+n+" bomb"+(n==1?"":"s")+" in the same row as "+boxes[y][c].code);
|
|
}
|
|
|
|
// Key code bigger or less than
|
|
for(i=0; i<6; i++){
|
|
var a = rand(33, 67);
|
|
var t = "The box with the key is ";
|
|
if(keyCode > a) t += "bigger than";
|
|
else if(keyCode < a) t += "less than";
|
|
else t += "equal to";
|
|
t += " "+a;
|
|
tips.push(t);
|
|
}
|
|
|
|
// Key code odd or even
|
|
tips.push("The key in the box with an "+(keyCode%2 == 0 ? "even" : "odd")+" number");
|
|
|
|
// Key not in same column as
|
|
for(i=0; i<6; i++){
|
|
var c;
|
|
do{
|
|
c = rand(0, 6);
|
|
}while(c == keyX);
|
|
var r = rand(0, 6);
|
|
|
|
tips.push("The key is not in the same column as "+boxes[r][c].code);
|
|
}
|
|
|
|
// Key not in same row as
|
|
for(i=0; i<6; i++){
|
|
var r;
|
|
do{
|
|
r = rand(0, 6);
|
|
}while(r == keyY);
|
|
var c = rand(0, 6);
|
|
|
|
tips.push("The key is not in the same row as "+boxes[r][c].code);
|
|
}
|
|
|
|
// Key not neighbour of
|
|
for(i=0; i<6; i++){
|
|
var x;
|
|
var y;
|
|
do{
|
|
x = rand(0, 6);
|
|
y = rand(0, 6);
|
|
}while(Math.abs(keyX - x) <= 1 && Math.abs(keyY - y) <= 1);
|
|
|
|
tips.push("The box with the key is not next to "+boxes[y][x].code);
|
|
}
|
|
|
|
|
|
// Player is not guard
|
|
for(i=0; i<userSockets.length; i++){
|
|
if(i == guardID) continue;
|
|
|
|
tips.push(userNames[i]+" is not the guard");
|
|
}
|
|
|
|
|
|
// Pick random tips for each tip box
|
|
for(y=0; y<6; y++){
|
|
for(x=0; x<6; x++){
|
|
if(boxes[y][x].type == BOX_TYPE_TIP){
|
|
var tipIndex = rand(0, tips.length);
|
|
boxes[y][x].tip = tips[tipIndex];
|
|
tips.splice(tipIndex, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log("Boxes created:");
|
|
console.log(boxes);
|
|
|
|
|
|
// Reset vars
|
|
currentUserID = -1; // Will increment on nextPlayer()
|
|
|
|
|
|
// Send game data
|
|
var dataUser = [];
|
|
for(i=0; i<userSockets.length; i++){
|
|
dataUser[i] = {guard:(i == guardID)};
|
|
}
|
|
|
|
for(i=0; i<userSockets.length; i++){
|
|
userSockets[i].emit("START", dataUser[i]);
|
|
}
|
|
|
|
console.log("Game data sent!");
|
|
|
|
// Reset OK
|
|
userOK = [];
|
|
for(i=0; i<userSockets.length; i++){
|
|
userOK[i] = false;
|
|
}
|
|
});
|
|
|
|
// OK
|
|
socket.on("OK", () => {
|
|
// Set OK for this player
|
|
userOK[userSockets.indexOf(socket)] = true;
|
|
|
|
// Check if everybody OK
|
|
var OK = true;
|
|
for(i=0; i<userOK.length; i++){
|
|
OK = OK && userOK[i];
|
|
}
|
|
|
|
// Send OK data
|
|
var waitingFor = [];
|
|
for(i=0; i<userOK.length; i++){
|
|
if(!userOK[i]) waitingFor.push(userNames[i]);
|
|
}
|
|
var dataOK = {users:waitingFor};
|
|
userSockets.forEach(function(s){s.emit("OK_REPLY", dataOK)});
|
|
|
|
// Perform next step if OK
|
|
if(OK){
|
|
// Send LOADING
|
|
userSockets.forEach(function(s){s.emit("LOADING");});
|
|
|
|
// Reset OK
|
|
userOK = [];
|
|
for(i=0; i<userSockets.length; i++){
|
|
userOK[i] = false;
|
|
}
|
|
|
|
// Next step
|
|
nextPlayer();
|
|
}
|
|
});
|
|
|
|
|
|
// PICK_REPLY
|
|
socket.on("PICK_REPLY", (code) => {
|
|
// When player picked box
|
|
if(currentUserID != userSockets.indexOf(socket)) return;
|
|
|
|
// Get box type
|
|
var type = null;
|
|
var x;
|
|
var y;
|
|
for(i=0; i<6; i++){
|
|
for(j=0; j<6; j++){
|
|
if(boxes[i][j].code == code){
|
|
type = boxes[i][j].type;
|
|
x = j;
|
|
y = i;
|
|
break;
|
|
}
|
|
}
|
|
if(type != null) break;
|
|
}
|
|
|
|
// Check type
|
|
if(type == BOX_TYPE_TIP || guardID == currentUserID){
|
|
// Return tip to user
|
|
var tip = boxes[y][x].tip;
|
|
|
|
var data = {myTurn:false, currentUser:userNames[currentUserID], tip:""};
|
|
for(i=0; i<userSockets.length; i++){
|
|
data.myTurn = (i == currentUserID);
|
|
data.tip = (i == currentUserID ? tip : "");
|
|
|
|
userSockets[i].emit("TIP", data);
|
|
}
|
|
console.log("Sent tip: "+tip);
|
|
|
|
}else if(type == BOX_TYPE_KEY){
|
|
// Prisoners win
|
|
var data = {win:true, board:boxes, reason:"The prisoners found the key to escape.", guard:userNames[guardID]};
|
|
|
|
// Send data
|
|
for(i=0; i<userSockets.length; i++){
|
|
data.win = (i != guardID);
|
|
|
|
userSockets[i].emit("STOP", data);
|
|
}
|
|
|
|
// Reset vars
|
|
state = STATE_LOBBY;
|
|
userSockets = [];
|
|
userNames = [];
|
|
|
|
}else if(type == BOX_TYPE_BOMB){
|
|
// Guard wins
|
|
var data = {win:false, board:boxes, reason:"The prisoners opened a box with a bomb.", guard:userNames[guardID]};
|
|
|
|
// Send data
|
|
for(i=0; i<userSockets.length; i++){
|
|
data.win = (i == guardID);
|
|
|
|
userSockets[i].emit("STOP", data);
|
|
}
|
|
|
|
// Reset vars
|
|
state = STATE_LOBBY;
|
|
userSockets = [];
|
|
userNames = [];
|
|
}
|
|
});
|
|
|
|
|
|
// TIP_REPLY
|
|
socket.on("TIP_REPLY", () => {
|
|
// When player pressed OK after tip
|
|
if(currentUserID != userSockets.indexOf(socket)) return;
|
|
|
|
// Next player
|
|
nextPlayer();
|
|
});
|
|
|
|
});
|
|
|
|
|
|
// Start HTTP server
|
|
server.listen(port, hostname, () => {
|
|
console.log("Server running at http://"+hostname+":"+port);
|
|
});
|
|
|
|
|
|
|
|
// Game methods
|
|
function nextPlayer(){
|
|
// Increment currentPlayerID
|
|
currentUserID = (currentUserID+1)%userSockets.length;
|
|
console.log("Next player = "+currentUserID+" ("+userNames[currentUserID]+")");
|
|
|
|
// Show board to every player and listen for one to pick a box
|
|
var board = [];
|
|
for(y=0; y<6; y++){
|
|
board[y] = [];
|
|
for(x=0; x<6; x++){
|
|
board[y][x] = {type:boxes[y][x].type, code:boxes[y][x].code};
|
|
}
|
|
}
|
|
var userData = {myTurn:false, board:board, currentUser:userNames[currentUserID]};
|
|
|
|
// Send userData
|
|
for(i=0; i<userSockets.length; i++){
|
|
userData.myTurn = (i == currentUserID);
|
|
userSockets[i].emit("PICK", userData);
|
|
}
|
|
console.log("PICK data sent");
|
|
}
|
|
|
|
|
|
|
|
// Helper methods
|
|
function rand(min, max){
|
|
return Math.floor(Math.random()*(max-min) + min);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|