/* 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 . Entre em contato comigo por e-mail via . */ 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. * @since 19 de outubro de 2020. */ novaPartida(){ this.estado = 'EM_PARTIDA'; // Popular a fila JFR. this.jf = []; let tmpJf = []; for (let x = 0; x < this.clientes.length; x++) if (this.clientes[x].apelido !== this.vezDe) tmpJf.push(this.clientes[x].apelido); for (let x = tmpJf.length; x > 0; x--){ // Pegar um jogador aleatório da fila. let num = Sala.gerarNúmeroAleatório(tmpJf.length); this.jf.push(tmpJf[num]); tmpJf.splice(num, 1); } 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.pei = []; // Palavras escolhidas inexistentes this.ldc = []; // Letras descobertas this.jpa = null; // Jogador que escolheu a palavra atual (Jogador Palavra Atual) this.palavra = null; 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. } /** * Retorna o próximo item da fila JF. * @returns String O próximo item da fila JF. * @since 02/01/2020 */ próxItemJf(){ let pv1 = this.jf[this.jf.indexOf(this.termoVezDe) + 1]; if (pv1 !== undefined) return pv1; return this.jf[0]; } /** * 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){ // Este termo já foi enviado? if (this.lee.indexOf(termo) !== -1 || this.lei.indexOf(termo) !== -1) resultadoTermo.resultado = "LETRA_JÁ_ESCOLHIDA"; // A palavra escolhida possui a letra enviada? else if (this.palavra.indexOf(termo) === -1){ this.lei.push(termo); resultadoTermo.resultado = "LETRA_INEXISTENTE"; } else { resultadoTermo.letrasPreenchidas = this.preencherCampos(termo); this.lee.push(termo); resultadoTermo.resultado = "LETRAS_PREENCHIDAS"; } return resultadoTermo; } else { // Esta palavra já foi enviada? if (this.pei.indexOf(termo) !== -1){ resultadoTermo.resultado = "PALAVRA_JÁ_ESCOLHIDA"; return resultadoTermo; } this.pei.push(termo); 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;