[Tutoriel] L'aléatoire en C/C++
Posté le 25/02/2014 18:17
Besoin d'un peu d'aléatoire dans tes programmes C/C++ ? Ce tuto est fait pour toi !
Prérequis :
Tout d'abord, la génération de nombres aléatoires de manière "classique" en C/C++ repose sur les fonctions
rand() et
srand(). Celles-ci sont incluses dans la bibliothèque
stdlib.h, il sera donc nécessaire de l'inclure dans le header de notre projet.
Générer des nombres aléatoires :
Pour générer un nombre aléatoire, le microprocesseur est incapable d'en déterminer un lui même. Il faut l'aider un peu, et c'est ce que l'on a fait en rentrant dans la calculatrice une fonction de ce type :
static unsigned int lastrandom=0x12345678;
unsigned int random(int seed = 0)
{
if(seed) lastrandom = seed;
lastrandom = ( 0x41C64E6D * lastrandom ) + 0x3039;
return (lastrandom >> 16);
}
Bref, lors de son premier appel, la fonction
rand() retournera alors le nombre de seed 0, puis celui dont le seed est la dernière valeur générée. On aura donc, par exemple
-> 0x 52 07 EB F8
-> 0x 72 65 C8 62
-> Etc.
Le problème, c'est que du coup, à chaque lancement du programme on aura les mêmes valeurs, puisque le seed original est le même...
Pour palier à cela, on détermine plus ou moins aléatoirement le seed avant le premier appel de la fonction avec
srand(int position). On ne l'utilisera qu'une seule fois, au tout début du programme.
Le plus dur dans tout ça, c'est de trouver un nombre qui soit suffisamment "aléatoire" pour qu'il soit différent à chaque fois. Pour cela, nous allons utiliser le syscall
RTC_getTicks().
RTC_getTicks() est une fonction très pratique qui retourne le nombre de ticks, c'est à dire de fractions (1/128) de seconde qui se sont écoulés depuis une certaine date (inconnue). Autant dire que la probabilité que le programme soit lancé deux fois en 1/128 seconde est minimale.
Voici le code à inclure :
static int SysCallCode[] = {0xD201422B,0x60F20000,0x80010070};
static int (*SysCall)(int R4, int R5, int R6, int R7, int FNo ) = (void*)&SysCallCode;
int RTC_getTicks()
{
return (*SysCall)(0, 0, 0, 0, 0x3B);
}
Pour résumer, générer un nombre aléatoire se fait de cette façon :
1) On initialise avec
srand(RTC_getTicks());
2) On récupère le nombre avec
rand() :
int nbAlea = rand();
Traitement du nombre aléatoire :
C'est bien joli, mais
rand() ne retourne qu'un nombre compris entre 0 et 2^32-1 = 4294967295. Et nous voudrions un nombre entier entre 0 et 10, par exemple. C'est pour cela que nous allons modifier ce nombre :
Nombre entier entre 0 et max :
Nous allons utiliser le modulo (%), opération très pratique qui retourne le résultat de la division euclidienne de a par b (a%b). Or, puisque que ce reste n'est jamais supérieur à b, il est forcément égal à 0, 1, 2, 3, ..., b-1. Nous pouvons donc faire, pour générer un nombre entre 0 (inclus) et max (exclus) :
int rand_int(int max)
{
return rand() % max;
}
Nombre entier entre min et max :
Toujours en utilisant le modulo, il est possible de bidouiller la fonction ci-dessus pour inclure le fait d'avoir un minimum :
int rand_int_ab(int min, int max)
{
return rand() % (max - min) + min;
}
On a donc un nombre entier aléatoire entre min (inclus) et max (exclus).
Nombre décimal entre min et max :
Le problème du modulo, c'est qu'il ne permet que de travailler sur des entiers. Nous allons donc devoir nous en passer pour la génération de nombre décimaux (sous forme de float).
Nous avons vu que
rand() retournait un nombre entre 0 et 2^32. Donc en supposant que
RAND_MAX = 2^32,
rand() / RAND_MAX est compris entre 0 et 1. Sachant que
RAND_MAX est déjà défini dans "time.h", on peut l'utiliser tel quel. On en déduit alors la fonction suivante :
float rand_float_ab(float min, float max)
{
return ( rand() / RAND_MAX ) * (max - min) + min;
}
Conclusion et annexes :
Ce tuto touche à son terme, j'espère qu'il t'aura été utile, et que tu as compris les bases de la fonction
rand().
Si toutefois tu souhaite plus d'informations, ou que tu as une question, n'hésite-pas à laisser un commentaire ci-dessous !
De plus, tu trouvera peut-être ton bonheur sur l'ancien Site du Zéro, et plus particulièrement
ici.
A bientôt sur Planète Casio !
Citer : Posté le 25/02/2014 18:53 | #
les fonctions rand() srand() [...] incluses dans la bibliothèque time.h
Petite erreur de ta part je crois
Ces fonctions font partie de stdlib. D'ailleurs c'est marqué dans le "C Standard Libraries.pdf"
Citer : Posté le 25/02/2014 20:11 | #
Au temps pour moi c'est corrigé
Citer : Posté le 25/02/2014 21:41 | #
A ajouter à liste des tutoriels
Vitesse des fonctions en Basic Casio | 7 days CPC | Casio Universal Wiki | Tutoriel Basic Casio
>>> Give me a click Brother <<< >>> Teste mon générateur de mots nouveaux <<<
>>> Random Youtube Video <<<
Citer : Posté le 26/02/2014 09:54 | #
Louloux, en regardant le code de Mipjabok, j'ai vu que tu utilisais "rand()%128" pour générer un nombre aléatoire entre 0 et 128 mais je n'ai pas vu d'initialisation, tu fais comment?
Citer : Posté le 26/02/2014 10:00 | #
L'initialisation de srand se fait souvent avec la fonction getTicks()
Citer : Posté le 26/02/2014 10:06 | #
Ok, je verrais comment il a fait quand je rentrerai chez moi.
Citer : Posté le 26/02/2014 18:41 | #
Sinon une autre astuce pour ne pas utiliser getTicks est de mettre le nombre de frames écoulées depuis le lancement du menu, par exemple
Citer : Posté le 26/02/2014 20:20 | #
Mais ça tu est obligé de le calculer toi-même non ?
Citer : Posté le 26/02/2014 20:36 | #
Pas forcément, par exemple :
int choix;
while(IsKeyUp(KEY_CTRL_EXE))
{
i++;
if(IsKeyDown(KEY_CHAR_1)) choix = 1;
if(IsKeyDown(KEY_CHAR_2)) choix = 2;
}
srand(i);
Citer : Posté le 26/02/2014 20:42 | #
Ç'est vrai que ça c'est du pur aléatoire
Mais je pense que je préfère quand même le getTicks()
Citer : Posté le 26/02/2014 20:51 | #
Ben dans notre cas, tu tu lance le jeu un grand nombre de fois, tu as des chances de retomber sur les mêmes valeurs de temps en temps, ce qui n'est pas le cas de RTC_getTicks()
Citer : Posté le 26/02/2014 20:54 | #
Et si tu mélange les deux, tu as un super tirage aléatoire !
Zelda de Smashmaster
Super Geek Brothers de Siapran
Pac-Man
Pac-Man Color
Meta Ball
Add-ins Jetpack Joyride et Pac-Man sur PRIZM (les 2 non commencés mais en réflexion)
A la recherche des sprites jetpack Joride si quelqu'un les a en couleur
Citer : Posté le 26/02/2014 20:55 | #
J'aimerais savoir comment tu voudrais mélanger les deux
Vu que srand efface la graine précédente
Citer : Posté le 26/02/2014 21:03 | #
Ben "srand(RTC_getTicks() + i);"
Citer : Posté le 26/02/2014 21:29 | #
Je pense que Louloux a l'habitude d'utiliser le nombre de frames écoulées depuis le lancement de l'add-ins.
(Il l'avait dit quelque part)
Jouez à 6 sur une seule calto : Curve Fever
Un die and retry qui vous fera bieeeen rager Test Andropov
un très bon sokoban
le seul vrai jeu de foot en basic : FIFA 12
Ca c'est ce que j'appelle un jeu de reflexion jewel master
Qui vaincra l'intelligence artificielle de cet othello
Le célèbre pacman
Et tant d'autres BTL's games
Le jeu du mois de Novembre et award du jeu le plus dur de l'année 2013 MultiTask, testez-le
Citer : Posté le 02/03/2014 19:30 | #
RTC_getTicks() est une fonction très pratique qui retourne le nombre de ticks, c'est à dire de fractions (1/128) de seconde qui se sont écoulés depuis une certaine date (inconnue)
Et y'a pas moyen de compter à l'envers le nombre de ticks et retomber sur la date (à moins que ce soit spécifique pour chaque calto, ce qui est probable en soir.. ) ?
D'accord c'est complètement inutile, mais simple curiosité.
Citer : Posté le 02/03/2014 19:49 | #
cette fonction renvoie le nombre de ticks écoulé depuis minuit (selon le processeur). Donc non, on ne peut retrouver la date a partir de cette fonction. Par contre, avec revolution-fx on peut le savoir
D'ailleurs, cette fonction renvoie au maximum 128*60*60*24 = 11059200, donc encore heureux qu'elle ne va pas plus loin que minuit, sinon on aurait des nombres très très très grand
envie de plonger dans la mer pour ramasser des tresors? => ballon sea
envie de sauver l'univers dans un jeu avec une longue durée de vie? => saviors of the future
un add-in addictif avec plein de secret et de trophées => evasion survival
un shmup bien dur et sadique => saviors 2
merci a tout le monde pour son soutien
zelda prizm de smashmaster (en esperant qu'il puisse le finir)
les tests de marmotti
un RPG de dark storm
(dont je connais le nom, mais pas vous )Arcuz !Citer : Posté le 02/03/2014 19:54 | #
D'accord, donc c'est depuis minuit déterminé à partir d'un instant 00:00h à un jour t arbitraire c'est ça ?
Il me semblait aussi que ça allait être très grand aussi
Citer : Posté le 02/03/2014 20:29 | #
En fait, c'est juste que le processeur est rarement réglé a la bonne heure, mais si il l’était, ce serait le nombre de ticks écoulé depuis minuit du jour actuel
envie de plonger dans la mer pour ramasser des tresors? => ballon sea
envie de sauver l'univers dans un jeu avec une longue durée de vie? => saviors of the future
un add-in addictif avec plein de secret et de trophées => evasion survival
un shmup bien dur et sadique => saviors 2
merci a tout le monde pour son soutien
zelda prizm de smashmaster (en esperant qu'il puisse le finir)
les tests de marmotti
un RPG de dark storm
(dont je connais le nom, mais pas vous )Arcuz !Citer : Posté le 03/03/2014 10:42 | #
Je pense que Louloux a l'habitude d'utiliser le nombre de frames écoulées depuis le lancement de l'add-ins.
(Il l'avait dit quelque part)
Désolé de vous décevoir, mais dans Mipjabok je n'initialise même pas ma fonction rand : je me fiche que le nuage de point quand on perd soit le même à chaque fois qu'on relance le jeu
Ajouté le 03/03/2014 à 10:43 :
Quant aux autres jeux, je prenais effectivement le nombre de frames écoulées depuis le lancement, histoire de pas m'embêter avec le RTC.