278 lines
9.3 KiB
JavaScript
278 lines
9.3 KiB
JavaScript
|
/*
|
||
|
Força: um (livre) clone do jogo Forca
|
||
|
Copyright (C) 2020 luca0N!
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
|
||
|
Entre em contato comigo por e-mail via <luca0n@luca0n.com>.
|
||
|
*/
|
||
|
|
||
|
class Sala {
|
||
|
/**
|
||
|
* Objeto Sala.
|
||
|
* @param string sala O nome da sala.
|
||
|
* @param Array A lista de clientes.
|
||
|
* @param string O apelido do líder.
|
||
|
* @param string O estado da sala.
|
||
|
*/
|
||
|
constructor(sala, clientes, líder, estado, opções){
|
||
|
this.sala = sala;
|
||
|
this.clientes = clientes;
|
||
|
this.opções = opções;
|
||
|
this.uas = Date.now(); // Última Ação Significante: marca o tempo em que um último evento significante tenha acontecido. Caso este evento tenha acontecido muito tempo atrás, esta sala será apagada.
|
||
|
this.líder = líder === undefined ? clientes[0].apelido : líder;
|
||
|
this.estado = estado === undefined ? "AGUARDANDO_JOGADORES" : estado;
|
||
|
this.pontuaçãoMeta = 0;
|
||
|
this.tin = 0; // tentativas incorretas
|
||
|
|
||
|
this.jfr = []; // fila de jogadores para rodadas
|
||
|
|
||
|
this.banidos = [];
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Inicia uma nova partida nesta sala.
|
||
|
* @returns null
|
||
|
* @since 19 de outubro de 2020.
|
||
|
*/
|
||
|
novaPartida(){
|
||
|
this.estado = 'EM_PARTIDA';
|
||
|
// Popular a fila JFR.
|
||
|
|
||
|
let tmpJfr = []; // JFR temporária, utilizado para popular a JFR verdadeira.
|
||
|
|
||
|
for (let x = 0; x < this.clientes.length; x++)
|
||
|
tmpJfr.push(this.clientes[x].apelido);
|
||
|
|
||
|
for (let x = tmpJfr.length; x > 0; x--){
|
||
|
// Pegar um jogador aleatório da fila.
|
||
|
let num = Sala.gerarNúmeroAleatório(tmpJfr.length);
|
||
|
this.jfr.push(tmpJfr[num]);
|
||
|
tmpJfr.splice(num, 1);
|
||
|
}
|
||
|
|
||
|
this.novaRodada();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inicia uma nova rodada nesta sala, escolhendo um jogador aleatório.
|
||
|
* @returns null
|
||
|
* @since 05 de novembro de 2020.
|
||
|
*/
|
||
|
novaRodada(){
|
||
|
this.lei = []; // Letras escolhidas inexistentes
|
||
|
this.lee = []; // Letras escolhidas existentes
|
||
|
this.ldc = []; // Letras descobertas
|
||
|
this.jpa = null; // Jogador que escolheu a palavra atual (Jogador Palavra Atual)
|
||
|
this.palavra = null;
|
||
|
this.jf = [];
|
||
|
this.tin = 0;
|
||
|
|
||
|
// Vamos escolher um jogador aleatório.
|
||
|
if (this.jfr.indexOf(this.vezDe) === this.jfr.length - 1)
|
||
|
// Fim da fila JFR atingido. Voltar para o começo.
|
||
|
this.vezDe = null;
|
||
|
|
||
|
this.vezDe = this.vezDe === undefined || this.vezDe === null ? this.jfr[0] : this.jfr[this.jfr.indexOf(this.vezDe) + 1]; // Escolher o primeiro jogador na fila caso o limite tenha sido atingido, ou escolher o próximo jogador na fila caso contrário.
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calcula e define a quantidade de letras na palavra atual.
|
||
|
* @since 23 de outubro de 2020.
|
||
|
*/
|
||
|
definirPalavraLetrasQtd(){
|
||
|
let letrasQtd = 0;
|
||
|
for (let x = 0; x < this.palavra.length; x++)
|
||
|
if (this.palavra[x] !== ' ')
|
||
|
letrasQtd++;
|
||
|
this.palavraLetrasQtd = letrasQtd;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Deduz se é possível que um jogador descubra a palavra atual baseado na quantidade de letras descobertas da palavra.
|
||
|
* @returns boolean Verdadeiro caso mais de 75% da palavra tenha sido descoberto ou caso apenas falte uma letra para descobrir a palavra inteira.
|
||
|
* @since 23 de outubro de 2020.
|
||
|
*/
|
||
|
possívelDescoberta(){
|
||
|
let ld = 0;
|
||
|
for (let x = 0; x < this.palavra.length; x++)
|
||
|
if (this.ldc[x] !== undefined)
|
||
|
ld++;
|
||
|
|
||
|
// Pelo menos 75% da palavra deve ter sido descoberta para retornar o evento de descoberta de palavra.
|
||
|
// Caso a palavra seja pequena, então pelo menos uma letra restante deve estar faltando.
|
||
|
|
||
|
if(ld / this.palavraLetrasQtd >= 0.75){
|
||
|
return true;
|
||
|
} else {
|
||
|
// Menos de 75% da palavra foi descoberta. Caso apenas uma letra esteja faltando, então retornar verdadeiro de qualquer maneira.
|
||
|
return this.palavraLetrasQtd - 1 === ld;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retorna uma lista contendo as posições dos espaços na palavra atual.
|
||
|
* @param String busca O alvo para procurar na palavra atual.
|
||
|
* @returns Array Lista contendo as posições dos espaços na palavra atual.
|
||
|
* @since 22 de outubro de 2020.
|
||
|
*/
|
||
|
procurar(busca){
|
||
|
let espaços = [];
|
||
|
for (var x = 0; x < this.palavra.length; x++){
|
||
|
if (this.palavra[x] === busca)
|
||
|
espaços.push(x);
|
||
|
}
|
||
|
return espaços;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Esta função processa o termo enviado por um jogador.
|
||
|
* @param string termo O termo enviado pelo jogador escolhido.
|
||
|
* @returns "LETRA_INEXISTENTE" caso a letra inserida não exista na palavra atual; "LETRAS_PREENCHIDAS" caso a letra inserida apareça na palavra atual.
|
||
|
* @since 19 de outubro de 2020.
|
||
|
*/
|
||
|
processarTermo(termo){
|
||
|
termo = termo.toUpperCase();
|
||
|
let resultadoTermo = { letrasPreenchidas: 0, resultado: null };
|
||
|
if (termo.length === 1){
|
||
|
// A palavra escolhida possui a letra enviada?
|
||
|
if (this.palavra.indexOf(termo) === -1)
|
||
|
resultadoTermo.resultado = 'LETRA_INEXISTENTE';
|
||
|
else {
|
||
|
resultadoTermo.letrasPreenchidas = this.preencherCampos(termo);
|
||
|
resultadoTermo.resultado = 'LETRAS_PREENCHIDAS';
|
||
|
}
|
||
|
return resultadoTermo;
|
||
|
} else {
|
||
|
let palavraCorreta = this.palavra === termo;
|
||
|
resultadoTermo.resultado = palavraCorreta ? "PALAVRA_CORRETA" : "PALAVRA_INCORRETA";
|
||
|
if (palavraCorreta){
|
||
|
for(var x = 0; x < this.palavra.length; x++)
|
||
|
if (this.ldc[x] === undefined)
|
||
|
resultadoTermo.letrasPreenchidas++;
|
||
|
}
|
||
|
}
|
||
|
return resultadoTermo;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gera um jogador aleatório. Esta função é utilizada para escolher a vez de alguma pessoa aleatoriamente sem ecolher pessoas que já foram escolhidas.
|
||
|
* @returns string O apelido do jogador escolhido.
|
||
|
* @since 19 de outubro de 2019.
|
||
|
*/
|
||
|
gerarJogadorAleatório(){
|
||
|
// Se todos os jogadores já tiverem sido escolhidos, então redefinir a fila de jogadores.
|
||
|
if (this.jf.length === this.clientes.length - 1)
|
||
|
this.jf = [];
|
||
|
|
||
|
var clientesPossíveis = [];
|
||
|
for (var x = 0; x < this.clientes.length; x++){
|
||
|
var ca = this.clientes[x]; // Cliente atual
|
||
|
if (ca.apelido !== this.jpa // A pessoa que escolheu a palavra não pode ser escolhida.
|
||
|
&& this.jf.indexOf(ca.apelido) === -1) { // Jogadores que já foram escolhidos não podem ser escolhidos novamente.
|
||
|
clientesPossíveis.push(ca.apelido);
|
||
|
}
|
||
|
}
|
||
|
var je = clientesPossíveis[Sala.gerarNúmeroAleatório(clientesPossíveis.length)]; // Jogador escolhido
|
||
|
return je;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gera um jogador aleatório para uma nova rodada.
|
||
|
* @returns String O apelido do jogador escolhido.
|
||
|
* @since 25 de dezembro de 2020.
|
||
|
*/
|
||
|
gerarJogadorAleatórioRodada(){
|
||
|
// Se todos os jogadores já tiverem sido escolhidos, então redefinir a fila de jogadores.
|
||
|
if (this.jfr.length === this.clientes.length - 1)
|
||
|
this.jfr = [];
|
||
|
|
||
|
let clientesPossíveis = [];
|
||
|
for (let x = 0; x < this.clientes.length; x++){
|
||
|
let ca = this.clientes[x]; // Cliente atual
|
||
|
if (this.jfr.indexOf(ca.apelido) === -1) // Jogadores que já foram escolhidos não podem ser escolhidos novamente.
|
||
|
clientesPossíveis.push(ca.apelido);
|
||
|
}
|
||
|
let je = clientesPossíveis[Sala.gerarNúmeroAleatório(clientesPossíveis.length)]; // Jogador escolhido
|
||
|
return je;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Preenche os campos com a letra especificada.
|
||
|
* @param string letra A letra desejada.
|
||
|
* @since 19 de outubro de 2020.
|
||
|
*/
|
||
|
preencherCampos(letra){
|
||
|
let qtd = 0; // quantidade de campos preenchidos
|
||
|
for (let x = 0; x < this.palavra.length; x++){
|
||
|
if (this.palavra[x] === letra){
|
||
|
this.ldc[x] = letra;
|
||
|
qtd++;
|
||
|
}
|
||
|
}
|
||
|
return qtd;
|
||
|
}
|
||
|
/**
|
||
|
* Retorna o vencedor da partida atual caso exista.
|
||
|
* @returns Object O cliente do jogador que venceu a partida ou nulo caso não há um vencedor.
|
||
|
* @since 23 de dezembro de 2020.
|
||
|
*/
|
||
|
receberVencedor(){
|
||
|
let pv = []; // Lista de possíveis vencedores. Adicionar jogadores que ultrapassaram a meta aqui. Caso mais de um cliente seja adicionado aqui, então conceder vitória ao jogador que tiver a maior quantidade de pontos ou retornar nulo caso houve um empate.
|
||
|
for (let x = 0; x < this.clientes.length; x++)
|
||
|
if (this.clientes[x].pontos >= this.pontuaçãoMeta)
|
||
|
pv.push(this.clientes[x]);
|
||
|
|
||
|
if (pv.length === 1)
|
||
|
return pv[0];
|
||
|
else if (pv.length === 0)
|
||
|
return null;
|
||
|
else {
|
||
|
pv.sort(function (a, b){
|
||
|
return b.pontos - a.pontos;
|
||
|
});
|
||
|
// Houve um empate?
|
||
|
return pv[0].pontos === pv[1].pontos ? null // Houve um empate; retornar nulo.
|
||
|
: pv[0]; // Não houve um empate. Retornar o cliente que ficou no topo.
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Checa se o endereço dado está banido nesta sala.
|
||
|
* @param String endereço O endereço remoto do cliente.
|
||
|
* @returns Verdadeiro caso o endereço esteja banido nesta sala, falso caso contrário.
|
||
|
* @since 24 de dezembro de 2020.
|
||
|
*/
|
||
|
banido(endereço){
|
||
|
for (let x = 0; x < this.banidos.length; x++)
|
||
|
if (this.banidos[x] === endereço)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gera um número aleatório baseado no argumento providenciado.
|
||
|
* @param int máximo O número máximo desejado.
|
||
|
* @since 15 de outubro de 2020.
|
||
|
*/
|
||
|
static gerarNúmeroAleatório(máximo){
|
||
|
var rnd = Math.random();
|
||
|
var num = Math.floor(rnd * máximo);
|
||
|
return num;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = Sala;
|