@hp4k1h5/terminordle
Version:
> multiplayer [wordle](https://www.powerlanguage.co.uk/wordle/) clone in your terminal
262 lines (261 loc) • 8.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.endSession = exports.again = exports.guess = exports.join = exports.createSession = exports.remove = exports.Session = exports.sessions = void 0;
const structs_1 = require("../../lib/structs");
const __1 = require("../../");
const args_1 = require("../../cli/args");
const msg_1 = require("./msg");
const util_1 = require("../../util");
exports.sessions = {};
const MAX_GUESSES = 20;
class Session {
constructor(session_id) {
this.session_id = session_id;
this.guests = [];
this.answer = 'ibudi';
this.guesses = [];
this.reset_lock = false;
}
}
exports.Session = Session;
function remove(ws, log) {
if (!ws.session_id)
return;
const userSession = exports.sessions[ws.session_id];
if (!userSession || !userSession.guests)
return;
const userIndex = userSession.guests.findIndex(guest => guest.id === ws.user_id);
if (userIndex === -1)
return;
userSession.guests.splice(userIndex, 1);
log &&
log.log({
removing: ws.user_id,
from: ws.session_id,
clients: args_1.wss.clients.size,
});
const guests = getGuests(userSession);
guests.forEach(guest => {
(0, msg_1.msg)(guest, {
type: structs_1.ClientMsgType.info,
content: `${ws.user_id} has left the game`,
});
});
// delete session if empty
if (userSession && userSession.guests.length === 0) {
delete exports.sessions[ws.session_id];
}
// free user name
if (ws.user_id && util_1.names[ws.user_id]) {
util_1.names[ws.user_id] = false;
}
// only close open(ing) connections
if (ws.readyState < 2) {
ws.close();
}
}
exports.remove = remove;
function createSession(ws, message, log) {
let session_id;
try {
session_id = sessionId();
}
catch (e) {
return;
}
exports.sessions[session_id] = new Session(session_id);
const answer = selectAnswer();
exports.sessions[session_id].answer = answer;
const response = {
type: structs_1.MsgType.session_id,
user_id: ws.user_id,
content: session_id,
session_id,
};
log && log.log({ session_id, answer });
join(ws, response);
}
exports.createSession = createSession;
const wordList = Object.keys(util_1.words);
function sessionId() {
let id;
let tries = 0;
const MAX_TRIES = 10;
while (!id && tries++ < MAX_TRIES) {
id = [(0, util_1.getRand)(wordList), '-', (0, util_1.getRand)(wordList)].join('');
if (!exports.sessions[id]) {
exports.sessions[id] = new Session(id);
return id;
}
id = undefined;
}
throw 'no session available';
}
const filteredWordList = wordList.filter(word => word.length === 5 && /[a-z]/i.test(word));
function selectAnswer() {
return (0, util_1.getRand)(filteredWordList);
}
function join(ws, message) {
if (!message || !message.session_id || !exports.sessions[message.session_id]) {
const e = {
type: structs_1.MsgType.error,
content: `no such session id ${message.session_id}, ${ws.user_id}`,
};
(0, msg_1.msg)(ws, e);
ws.close();
throw e;
}
const session = exports.sessions[message.session_id];
// add guess to session
session.guests.push({ id: ws.user_id });
// update ws
ws.session_id = message.session_id;
// send session id confirmation
const response = {
type: structs_1.MsgType.session_id,
user_id: message.user_id,
session_id: message.session_id,
content: message.session_id,
};
(0, msg_1.msg)(ws, response);
// update other players
const guests = getGuests(exports.sessions[message.session_id]);
guests.forEach(guest => {
(0, msg_1.msg)(guest, { type: structs_1.MsgType.info, content: `${ws.user_id} joined!` });
});
// replay guesses back to client
exports.sessions[message.session_id].guesses.forEach((guess, i) => {
(0, msg_1.msg)(ws, {
type: structs_1.MsgType.guess,
content: { guess, rem: MAX_GUESSES - (i + 1) },
});
});
}
exports.join = join;
function guess(ws, message) {
try {
(0, __1.validateResponse)(message);
}
catch (e) {
(0, msg_1.err)(ws, e);
return;
}
if (!message.session_id) {
(0, msg_1.err)(ws, 'no session_id');
return;
}
const session = exports.sessions[message.session_id];
if (!session) {
(0, msg_1.err)(ws, 'no such session_id');
return;
}
const guess = (0, __1.wordToRow)(message.content);
(0, __1.evaluateGuess)(guess, (0, __1.wordToRow)(session.answer));
session.guesses.push(guess);
// broadcast guess to session guests
const sessionGuests = getGuests(session);
const response = {
type: structs_1.ClientMsgType.guess,
content: { guess, rem: MAX_GUESSES - session.guesses.length },
};
const correct = (0, __1.isCorrect)(guess);
// game over free lock
if (correct)
session.reset_lock = false;
sessionGuests.forEach(guest => {
// update client
(0, msg_1.msg)(guest, response);
// check win condition
if (correct) {
const winMsg = {
...message,
type: structs_1.ClientMsgType.again,
content: ws.user_id,
user_id: ws.user_id,
};
(0, msg_1.msg)(guest, winMsg);
}
else if (session.guesses.length >= MAX_GUESSES) {
const lossMsg = {
type: structs_1.ClientMsgType.again,
content: `out of guesses (20)
ANSWER: ${session.answer}`,
};
// update client
(0, msg_1.msg)(guest, lossMsg);
return;
}
});
}
exports.guess = guess;
function again(cnx, message, log) {
if (!message || typeof message.content !== 'string' || !cnx.session_id) {
remove(cnx, log);
return undefined;
}
if (!/^y/i.test(message.content)) {
(0, msg_1.msg)(cnx, {
type: structs_1.ClientMsgType.info,
content: 'goodbye',
});
remove(cnx, log);
return;
}
log &&
log.log({
again: message.content,
user_id: cnx.user_id,
});
// get session
const session = exports.sessions[cnx.session_id];
// another session member has reset the session
if (session.reset_lock && cnx.session_id) {
// replay guesses back to client
session.guesses.forEach((guess, i) => {
(0, msg_1.msg)(cnx, {
type: structs_1.MsgType.guess,
content: { guess, rem: MAX_GUESSES - (i + 1) },
});
});
return;
}
// first to play again resets the session
session.reset_lock = true;
// reset_lock is freed by win/loss condition
// reset answer
const answer = selectAnswer();
session.answer = answer;
// reset guesses
session.guesses = [];
log && log.log({ session_id: cnx.session_id, answer });
// client side reset is handled client side on decision
}
exports.again = again;
function endSession(cnx, message) {
if (!cnx.session_id)
return;
const session = exports.sessions[cnx.session_id];
if (!session) {
(0, msg_1.err)(cnx, message);
// still boot the connection
remove(cnx);
return;
}
// boot all session guests from server
const guests = getGuests(session);
guests.forEach(guest => {
remove(guest);
});
}
exports.endSession = endSession;
function getGuests(session) {
const sessionGuests = [];
args_1.wss.clients.forEach((client) => {
if (session.guests.find(guest => {
return guest.id === client.user_id;
})) {
sessionGuests.push(client);
}
});
return sessionGuests;
}