O TEMA DO FÓRUM ESTÁ EM MANUTENÇÃO. FEEDBACKS AQUI: ACESSAR

Duvida simples sobre variáveis/gerar números aleatórios

Iniciado por Crewyvern, 12/05/2020 às 13:51

Boa tarde pessoas! Uma coisa que sempre colocou uma "duvidazinha" na minha cabeça foi, gerar números aleatórios, hoje fui entrar em um projeto do MV, e me deparei com um sistema de gerar monstros por números aleatórios, a tempos que venho testando e observando quais números caem para ter uma noção de quais números caem mais e etc. Na variável coloquei para gerar de 1..30, sendo que desses 30, 6 eram condições para criar um novo monstro, cheguei a testar várias vezes e pelo que parece alguns números nunca caem, e é quase um padrão os que aparecem, a minha duvida é, por scripts seria melhor ou daria na mesma?

Pelos meus testes,trocando de mapa e reentrando no mesmo, resetava o evento e os números caiam  iguais mais de duas vezes.
E eu sei que a ideia é gerar um número aleatório, e que pode cair o mesmo algumas vezes, mas ao meu ver é meio falho.

Quem puder tirar essa duvida agradeceria. \o/
Depressão sem obsessão, pulsos livres da dor, tristeza confusa, felicidade sem sentido, ânimo e desânimo, raiva momentânea e a solidão sempre presente...até que a morte os separe, amém.

Assim, existem vários algoritmos pra geração de números aleatórios. Alguns jogos usam pseudo number generators, ou PRNG (no caso de pokémon), estes pegam uma semente inicial (gerada via cálculos matemáticos) e geram números a partir dele. Se você conseguir a semente é possível abusar do RNG. Outros jogos usam true random number generator, ou TRNG, esse é o mais difícil de abusar pois eles pegam a semente por meios fisicamente estranhos, como barulho atmosférico, temperatura ou coisa do tipo.

A maioria das linguagens possuem um meio padrão de se obter números aleatórios. A classe Random em ruby usa PRNG:
Código: Ruby
number = rand(30)
# ou
prng = Random.new # cria um rng separado do padrão, possivelmente com uma semente customizada

Recomendo que veja mais sobre ele na documentação.

Math.random() em javascript por padrão gera números aleatórios usando uma semente baseada no tempo atual, assim como Java.
Código: Javascript
let number = Math.random(); // gera um número entre 0 (inclusive) e 1 (exclusivo).

O problema aqui é que você não pode gerar números aleatórios usando sua própria semente, como em ruby. Você provavelmente vai ter que trabalhar em cima disso usando outras libs ou algum outro algoritmo customizado, como esse aqui pra compensar.

12/05/2020 às 15:54 #2 Última edição: 12/05/2020 às 15:58 por Brandt
Edit: Comecei a escrever antes do syureri, fiquei com dó de jogar o post fora ahsuahsu
O Mersenne Twister é um algoritmo maneiro pra RNG, se for seguir com a ideia do PRNG acredito que seja a melhor escolha, assim como o artigo que eu referenciei e várias respostas no SO apontam. Espero que a minha resposta contribua em algo também xd


Então, o que acontece é que computadores são fundamentalmente ruins em gerar números aleatórios. Na verdade, o número aleatório que é gerado aí é um pseudo-aleatório: você começa com um valor inicial (que chamamos de seed), e então aplica um algoritmo pra gerar os próximos números de forma que eles seja muito difícil ou impossível gerar a mesma sequência dados dois seeds diferentes. Se seu algoritmo de geração de números aleatórios for criptográfico, tem ainda o requisito de ser praticamente impossível prever o próximo número.

O que acontece no MV quando você usa o comando definir variável aleatório é que ele usa uma dessas funções, definida aqui. Então, pra encurtar: não vai ser melhor com scripts, porque os eventos já fazem o que um script faria.

Esse artigo dá uma leitura interessante e comenta, com muito mais expertise do que eu poderia comentar, sobre os problemas com o gerador de números aleatórios do JS.

Ele sugere algumas soluções, melhores que a do próprio RPG Maker, também:
- randint.js, uma implementação que faz alguns tratamentos a mais além do floor pra gerar um número aleatório mais confiável;
- usar Crypto.getRandomValues, que gera valores aleatórios criptográficos; esse método deve ser um pouco mais lento, mas dado que você não vai chamar o gerador a cada frame é pra lá de aceitável.

Uma implementação com essa segunda opção seria:

Math.randomInt = function(max) {
  let a = new Uint32Array(1);
  crypto.getRandomValues(a);
  return a[0] % max;
};


(Só colar isso num plugin já deve funcionar)

Isso já deve te dar um algoritmo de RNG mais satisfatório que o do JS em termos técnicos, com uma distribuição mais uniforme.




Outra alternativa, se você não quiser necessariamente depender de um RNG pra que algo aconteça pelo menos uma vez ou mais, é usar uma lista com todas as possibilidades e simplesmente embaralhar ela, mudando assim apenas a ordem do que acontece, mas ainda garantindo que todas as possibilidades vão ocorrer em algum momento. Isso pode ser feito usando um algoritmo de embaralhamento:

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;
}


Fonte: https://stackoverflow.com/a/2450976/9629768

Com isso você pode definir, por exemplo, uma lista de IDs monstros, e cada vez que entrar no mapa/usar todos os itens da lista você embaralha ela de novo (exemplo de código, meio feio):

const monsters = [1, 1, 1, 3, 5, 5, 9, 10]; // As repetições aumentam a chance para um determinado inimigo
let currentMonster = monsters.length;

//...

// Para usar:

currentMonster++;

if (currentMonster >= monsters.length) {
  shuffle(monsters);
  currentMonster = 0;
}

let monster = monsters[currentMonster]; // monster é o ID do seu inimigo


Isso garante que os encontros vão acontecer conforme a distribuição que você determinar, e nunca repetindo vezes demais ou deixando valores de fora. Não é mais tão aleatório, mas passa a impressão que é mais aleatório que antes até, dependendo de quem vê hahaha
~ Masked

Você pode armazenar o ID em uma variável e verificar se o resultado do range aleatório foi igual a essa variável; se sim, repete o processo. Isso permite que o último ID não se repita, maaas... pode impactar a jogabilidade.
Unique Destiny | um novo conceito de RPG Maker 2000!

Citação de: Avenger_B7 online 12/05/2020 às 16:30
Você pode armazenar o ID em uma variável e verificar se o resultado do range aleatório foi igual a essa variável; se sim, repete o processo. Isso permite que o último ID não se repita, maaas... pode impactar a jogabilidade.

Isso não resolve o problema de alguns elementos ficarem excluídos. Suponha o conjunto { 1, 2, 3 } e um RNG beeeem ruim, que seja enviesado para o 1 e 2.

O algoritmo que você propôs foi:

valor = random()  // Valor aleatório
aux = valor

until valor != aux:
  valor = random()


Se você rodar isso, vai ter um loop de 1 e 2, e raramente um 3.

Claro, é um exemplo meio extremo, mas dá pra imaginar como ficaria a situação dado o que acontece de fato. Resolve as repetições (apesar que só as imediatas, ainda dá pra ficar oscilando), mas não os inimigos que nunca são sorteados.
~ Masked

Cheguei a testar esses comandos em java, tentei fazer algo bem parecido com o que vocês mostraram, alguns eu entendi como funciona, outros eu fiquei meio bugado. Salvei os links que vocês me mandaram, e vou tentar entender aos poucos, juntamente com o que venho aprendendo em java, já que js e java são um pouco parecidos, acho que mais pra frente eu consigo entender. Fiz os testes no eclipse IDE, alguns comandos tinham, porém não sabia ao certo como funcionava, não chegou a dar erro, mas não printou nada, talvez eu tenha feito algo de errado, e os funcionaram deu nisso ai, esses eu entendi.

package yedy7ed;
import java.util.Random;

public class yedy7ed{
	
	public static double monster;
	
	public static boolean monster1;
	public static double monster2 = 1.2;
	
	
	public static int numb;
	public static void main(String[] args) {
		Random rand = new Random();
		numb = rand.nextInt(30);
		System.out.println(numb);
		System.out.println(Math.random());
		
		monster = Math.random();
		System.out.println(monster);
		
		monster = Math.random();
		System.out.println(monster+Math.random());
		return;
	}
}


Eu já cheguei a usar Random()nextInt(Um valor + outro), mas nesse caso foi para gerar um ângulo, pelo que entendi;

int ang = new Random().nextInt(valor+valor);
		x = Math.cos(Math.toRadians(ang));
		y = Math.sin(Math.toRadians(ang));


Não sei esse também se encaixa na minha duvida, mas eu vou testar, e tentar juntar com o que vocês me passaram. Desculpa não ter entendido tudo, mas com certeza me ajudou bastante, consegui absorver uma boa parte do que foi dito.

Obrigado a todos que responderam minhas duvidas. :XD:
Depressão sem obsessão, pulsos livres da dor, tristeza confusa, felicidade sem sentido, ânimo e desânimo, raiva momentânea e a solidão sempre presente...até que a morte os separe, amém.