1
0
James Ketrenos a403f38f1c Selection of players working better
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
2022-01-27 20:41:59 -08:00

411 lines
12 KiB
JavaScript
Executable File

"use strict";
const express = require("express"),
config = require("config"),
moment = require("moment"),
crypto = require("crypto"),
util = require("util"),
Promise = require("bluebird"),
{ readFile, writeFile } = require("fs").promises;
let gameDB;
require("../db/games").then(function(db) {
gameDB = db;
});
const router = express.Router();
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
const assetData = {
tiles: [
{ type: "wood", y: 0. / 4. },
{ type: "wood", y: 1. / 4. },
{ type: "wood", y: 2. / 4. },
{ type: "wood", y: 3. / 4. },
{ type: "wheat", y: 0. / 4. },
{ type: "wheat", y: 1. / 4. },
{ type: "wheat", y: 2. / 4. },
{ type: "wheat", y: 3. / 4. },
{ type: "stone", y: 0. / 4. },
{ type: "stone", y: 1. / 4. },
{ type: "stone", y: 2. / 4. },
{ type: "sheep", y: 0. / 4. },
{ type: "sheep", y: 1. / 4. },
{ type: "sheep", y: 2. / 4. },
{ type: "sheep", y: 3. / 4. },
{ type: "brick", y: 0. / 4. },
{ type: "brick", y: 1. / 4. },
{ type: "brick", y: 2. / 4. },
{ type: "robber", y: 0 }
],
pips: [
{ roll: 7, pips: 0, y: 3. / 6., x: 0. / 6. },
{ roll: 5, pips: 4, y: 0. / 6., x: 0. / 6. },
{ roll: 2, pips: 1, y: 0. / 6., x: 1. / 6. },
{ roll: 6, pips: 5, y: 0. / 6., x: 2. / 6. },
{ roll: 3, pips: 2, y: 0. / 6., x: 3. / 6. },
{ roll: 8, pips: 5, y: 0. / 6., x: 4. / 6. },
{ roll: 10, pips: 3, y: 0. / 6., x: 5. / 6. },
{ roll: 9, pips: 4, y: 1. / 6., x: 0. / 6. },
{ roll: 12, pips: 1, y: 1. / 6., x: 1. / 6. },
{ roll: 11, pips: 2, y: 1. / 6., x: 2. / 6. },
{ roll: 4, pips: 3, y: 1. / 6., x: 3. / 6. },
{ roll: 8, pips: 5, y: 1. / 6., x: 4. / 6. },
{ roll: 10, pips: 3, y: 1. / 6., x: 5. / 6. },
{ roll: 9, pips: 4, y: 2. / 6., x: 0. / 6. },
{ roll: 4, pips: 3, y: 2. / 6., x: 1. / 6. },
{ roll: 5, pips: 4, y: 2. / 6., x: 2. / 6. },
{ roll: 6, pips: 6, y: 2. / 6., x: 3. / 6. },
{ roll: 3, pips: 2, y: 2. / 6., x: 4. / 6. },
{ roll: 11, pips: 2, y: 2. / 6., x: 5. / 6. }
],
borders: [
{ file: 'borders-1.6.png', left: "sheep", right: "bank" },
{ file: 'borders-2.1.png', center: "sheep" },
{ file: 'borders-3.2.png', left: "wheat", right: "bank" },
{ file: 'borders-4.3.png', center: "wood" },
{ file: 'borders-5.4.png', left: "sheep", right: "bank" },
{ file: 'borders-6.5.png', center: "bank" }
],
developmentCards: []
};
for (let i = 0; i < 14; i++) {
assetData.developmentCards.push("knight");
}
for (let i = 0; i < 6; i++) {
assetData.developmentCards.push("progress");
}
for (let i = 0; i < 5; i++) {
assetData.developmentCards.push("victoryPoint");
}
const games = {};
const roll = (game, player) => {
let error;
if (!player) {
error = "No player active; roll has no action";
console.log(error);
return error;
}
const name = player.name;
switch (game.state) {
case "lobby":
if (player.order) {
error = `Player ${name} already rolled for order.`;
console.log(error);
return error;
}
game.dice = [ Math.ceil(Math.random() * 6) ];
player.order = game.dice[0];
const message = `${name} rolled ${game.dice[0]} for play order.`;
game.chat.push({ date: Date.now(), message: message });
console.log(message);
return;
}
error = `Invalid game state (${game.state}) in roll.`;
return error;
}
router.put("/:id/:action/:value?", (req, res) => {
console.log(`PUT games/${req.params.id}/${req.params.action}`);
if (!(req.params.id in games)) {
const error = `Game not found: ${req.params.id}`;
return res.status(404).send(error);
}
const game = games[req.params.id];
let error;
if (req.params.action == "player-name") {
if (!req.params.value) {
error = `No name provided`;
} else {
req.session.playerName = req.params.value;
const color = req.session.playerColor;
if (color && game && color in game.players) {
game.players[color].name = req.session.playerName;
game.chat.push({ date: Date.now(), message: `${color} is no known as ${req.session.playerName}.` });
}
}
return sendGame(res, req, game, error);
}
if (req.params.action == "player-selected") {
if (!game) {
error = `No game found`;
return sendGame(res, req, game, error);
}
const selected = req.params.value;
let player;
console.log(`player-selected: ${selected}`);
/* Deselect currentlyi active player for this session */
for (let key in game.players) {
if (key == req.session.playerColor && selected != key) {
player = game.players[key];
if (player.session == req.session.id) {
player.session = '';
player.name = '';
player.status = 'Not active';
req.session.playerColor = '';
game.chat.push({ date: Date.now(), message: `${key} is no longer claimed.` });
break;
}
}
}
if (selected in game.players) {
player = game.players[selected];
player.session = req.session.id;
player.name = req.session.playerName;
player.status = `Just joined`;
req.session.playerColor = selected;
game.chat.push({ date: Date.now(), message: `${selected} is now '${player.name}'.` });
}
return sendGame(res, req, game, error);
}
if (!req.session.playerColor || !(req.session.playerColor in game.players)) {
error = `Invalid player: ${req.session.playerColor}`;
return sendGame(res, req, game, error);
}
player = game.players[req.session.playerColor];
const name = player.name;
switch (req.params.action) {
case "roll":
error = roll(game, player);
break;
case "shuffle":
if (game.state === "lobby") {
shuffleBoard(game);
const message = `${name} requested a new board.`;
game.chat.push({ date: Date.now(), message: message });
console.log(message);
} else {
error = `Game no longer in lobby (${game.state}). Can not shuffle board.`;
}
case "state":
const state = req.params.value ? req.params.value : "active";
if (state != game.state) {
game.state = state;
const message = `${name} set game state to ${state}.`;
game.chat.push({ date: Date.now(), message: message });
}
}
return sendGame(res, req, game, error);
})
router.get("/:id", async (req, res/*, next*/) => {
const id = req.params.id;
console.log("GET games/" + id);
let error, game;
if (id in games) {
game = games[id];
return sendGame(res, req, game)
}
if (/^\.|\//.exec(id)) {
error = `Requested game ID is invalid`;
console.log(error, id);
return res.status(400).send(error);
}
game = await readFile(`games/${id}`)
.catch(() => {
return;
});
if (!game) {
error = `Game ${id} not found -- returning invalid game state.`;
console.warn(error);
game = { id: id, state: 'invalid' };
} else {
game = JSON.parse(game);
}
games[id] = game;
return sendGame(res, req, game, error);
});
router.put("/:id", (req, res/*, next*/) => {
console.log("PUT games/" + req.params.id);
if (req.params.id in games) {
const game = games[req.params.id],
changes = req.body;
console.log(req.session.id, req.session.playerColor);
console.log(JSON.stringify(changes, null, 2));
for (let change in changes) {
switch (change) {
case "players":
break;
case "chat":
console.log("Chat change.");
game.chat.push({
from: changes.chat.player,
date: Date.now(),
message: changes.chat.message
});
if (game.chat.length > 10) {
game.chat.splice(0, game.chat.length - 10);
}
break;
}
}
return sendGame(res, req, game);
} else {
const error = `Game not found: ${req.params.id}`;
return res.status(404).send(error);
}
});
const sendGame = async (res, req, game, error) => {
let active = 0;
for (let player in game.players) {
player = game.players[player];
active += ((player.status && player.status != 'Not active') ? 1 : 0);
}
if (active < 2 && game.state != 'lobby' && game.state != 'invalid') {
let message = "Insufficient players in game. Setting back to lobby."
game.chat.push({ date: Date.now(), message: message });
console.log(message);
game.state = 'lobby';
}
await writeFile(`games/${game.id}`, JSON.stringify(game, null, 2))
.catch((error) => {
console.error(`Unable to write to games/${games.id}`);
console.error(error);
});
const playerGame = Object.assign({}, game, {
timestamp: Date.now(),
status: error ? error : "success",
activePlayerName: (req.session && req.session.playerName) ?
req.session.playerName : "",
activePlayer: (req.session && req.session.playerColor) ?
req.session.playerColor : null
});
if (game.id == 'b3c4bd15efe212a2') {
// console.log(req.session);
}
return res.status(200).send(playerGame);
}
router.post("/:id?", (req, res/*, next*/) => {
console.log("POST games/");
const id = req.params.id;
if (id && id in games) {
const error = `Can not create new game for ${id} -- it already exists.`
console.error(error);
return res.status(400).send(error);
}
const game = {
startTime: Date.now(),
state: "lobby", /* lobby, in-game, finished */
tiles: [],
pips: [],
borders: [],
tokens: [],
players: {
R: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" },
O: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" },
B: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" },
W: { roads: 15, cities: 4, settlements: 5, points: 0, name: "", status: "Not active" }
},
developmentCards: assetData.developmentCards.slice(),
dice: [ 0, 0 ],
sheep: 19,
ore: 19,
wool: 19,
brick: 19,
wheat: 19,
longestRoad: null,
largestArmy: null,
chat: [ { from: "R", date: Date.now(), message: "Server initialized!" } ],
id: id ? id : crypto.randomBytes(8).toString('hex')
};
games[game.id] = game;
req.session.playerColor = null;
shuffleBoard(game);
console.log(`New game created: ${game.id}`);
return sendGame(res, req, game);
});
const shuffleBoard = (game) => {
[ "tiles", "pips", "borders" ].forEach((field) => {
game[field] = []
for (let i = 0; i < assetData[field].length; i++) {
game[field].push(i);
}
/* Shuffle an array of indexes */
shuffle(game[field]);
/* Convert from an index array to a full array */
for (let i = 0; i < assetData[field].length; i++) {
game[field][i] = assetData[field][game[field][i]];
}
});
shuffle(game.developmentCards)
}
/*
return gameDB.sequelize.query("SELECT " +
"photos.*,albums.path AS path,photohashes.hash,modified,(albums.path || photos.filename) AS filepath FROM photos " +
"LEFT JOIN albums ON albums.id=photos.albumId " +
"LEFT JOIN photohashes ON photohashes.photoId=photos.id " +
"WHERE photos.id=:id", {
replacements: {
id: id
}, type: gameDB.Sequelize.QueryTypes.SELECT,
raw: true
}).then(function(photos) {
if (photos.length == 0) {
return null;
}
*/
if (0) {
router.get("/*", (req, res/*, next*/) => {
return gameDB.sequelize.query(query, {
replacements: replacements, type: gameDB.Sequelize.QueryTypes.SELECT
}).then((photos) => {
});
});
}
module.exports = router;