Page 1 sur 1

[WeiDU] Comment fonctionne la commande RANDOM de WeiDU ?

Posté : dim. 19 juil. 2015, 17:12
par Freddy_Gwendo
En phase de développement, j'ai créé plusieurs macros et fonctions pour automatiser la création de plusieurs fichiers spécifiques. Parmi elles, une procédure permet de générer des créatures sans me soucier de leurs caractéristiques, par exemple des PNJ non recrutables, mais nécessaires pour le scénar.

Code : Tout sélectionner

/*-----------------------------------------------------------------------------------*
 * MACRO GW_CRE_STATS_RANDOM : Création aléatoire des caractéristiques des créatures *
 *-----------------------------------------------------------------------------------*/

DEFINE_PATCH_FUNCTION ~GW_CRE_STATS_RANDOM~ 
	RET	GW_str_random
		GW_str_ex_random
		GW_dex_random
		GW_int_random
		GW_wis_random
		GW_con_random
		GW_chr_random
BEGIN

	SET GW_str_random = 0
	SET GW_str_ex_random = RANDOM (1 100)
	SET GW_dex_random = 0
	SET GW_int_random = 0
	SET GW_wis_random = 0
	SET GW_con_random = 0
	SET GW_chr_random = 0
	
	PATCH_FOR_EACH random_stat IN ~GW_str_random~ ~GW_dex_random~ ~GW_con_random~ ~GW_int_random~ ~GW_wis_random~ ~GW_chr_random~ BEGIN
		FOR (i = 0; i < 3; i += 1) BEGIN
			SET EVALUATE_BUFFER ~%random_stat%~ += RANDOM (1 6)	// 3 x 6 = 18 !
		END
	END
// exceptional strength (only for warriors with 18 strength)
	PATCH_IF (%GW_str_random% != 18) BEGIN
		SET GW_str_ex_random = 0
	END ELSE BEGIN
		PATCH_IF  (("%GW_class_lib%" STRING_CONTAINS_REGEXP "FIGHTER" != 0) && ("%GW_class_lib%" STRING_CONTAINS_REGEXP "RANGER" != 0) 
				&& ("%GW_class_lib%" STRING_CONTAINS_REGEXP "PALADIN" != 0)) BEGIN
			SET GW_str_ex_random = 0
		END
	END
	PATCH_PRINT ~CONTROLE RANDOM STATS : random_str = %GW_str_random% - random_str_ex = %GW_str_ex_random% - random_dex = %GW_dex_random% - random_con = %GW_con_random% - random_int = %GW_int_random% - random_wis = %GW_wis_random% - random_chr = %GW_chr_random%~
//    LPF GW_CRE_STATS_ADJUST	INT_VAR GW_str_random GW_str_ex_random GW_dex_random GW_int_random GW_wis_random GW_con_random GW_chr RET GW_str_ajust GW_str_ex_ajust GW_dex_ajust GW_int_ajust GW_wis_ajust GW_con_ajust GW_chr_ajust END // adjust statistics to match class and racial minimums, maximums and modifiers

END
Cette fonction semblait fonctionner jusqu'à ce que je m'aperçoive qu'elle générait toujours les mêmes caractéristiques à chaque installation (les valeurs pouvaient varier d'une installation à l'autre, mais étaient identiques pour toutes les créatures créées).

Par exemple :

CONTROLE RANDOM : random_str = 10 - random_str_ex = 0 - random_dex = 14 - random_con = 11 - random_int = 11 - random_wis = 6 - random_chr = 12

Après avoir trituré la doc WeiDU, j'ai fini par trouver une solution en ajoutant ces lignes jaunes dans la fonction :

Code : Tout sélectionner

DEFINE_PATCH_FUNCTION ~GW_CRE_STATS_RANDOM~ 
	RET	GW_str_random
		GW_str_ex_random
		GW_dex_random
		GW_int_random
		GW_wis_random
		GW_con_random
		GW_chr_random
BEGIN

[color="#FFFF00"]	INNER_ACTION BEGIN
		RANDOM_SEED null // Initialize random numbers
	END[/color]
	SET GW_str_random = 0
...
Problème : je ne comprends pas pourquoi ça fonctionne et quelle est l'utilité de la commande RANDOM_SEED, si ce n'est qu'elle semble solutionner mon problème. Et je déteste ne pas comprendre ce que je fais. :shok:

Alors si quelqu'un a une explication, je suis preneur. ;)

Posté : dim. 19 juil. 2015, 18:01
par Isaya
La graine d'un générateur aléatoire (en réalité pseudo-aléatoire) est l'initialisation permettant d'obtenir une séquence répétable entre deux utilisations. A défaut d'un forçage par le programmeur, cette initialisation est souvent faite avec un élément fonction de l'instant initial.

Je ne sais pas à quoi correspond null pour WeiDU mais, d'après la documentation, si ce n'est pas considéré comme un nombre, WeiDU va l'initialiser en fonction de l'heure. De sorte qu'en deux appels de ta fonction, il y a des chances que cela force la génération de séries de valeurs différentes. Dans cette hypothèse, tu pourrais parfaitement te passer de réinitialiser à null dans chaque appel de fonction, le générateur devrait continuer tranquillement à dérouler sa séquence de valeurs aléatoires, qui normalement a un cycle long.

Dans le cas où, comme dans certains languages, null est associé au nombre 0, cela devrait même te donner systématiquement toujours la même séquence à chaque appel de fonction, toutes tes créatures ayant exactement les mêmes caractéristiques.

Le problème que tu décris est pourtant l'inverse en l'absence de l'initialisation dans la fonction. C'est à croire que tu aurais quelque part ailleurs un appel à RANDOM_SEED entre deux appels de fonctions.

Il faut savoir qu'un générateur aléatoire est une entité complexe et que les langages ont longtemps intégré des fonctions assez mauvaises. A titre d'exemple, le générateur aléatoire de l'Infinity Engine vis à vis des RESPONSE #n des scripts est manifestement plutôt mauvais. J'ignore si WeiDU ou le langage sur lequel il s'appuie est meilleur.
En tout état de cause, à moins d'un sérieux bug dans WeiDU ou d'un RANDOM_SEED mal placé ailleurs dans ton code, tu ne devrais que très rarement générer les mêmes 6 caractéristiques pour 2 appels de fonction à partir de la première mouture de la fonction. Selon comment WeiDU considère null, la deuxième est encore plus risquée.
Peut-être que tu pourrais simplement te contenter d'ajouter une initialisation au tout début de ton programme d'installation. En principe, pour une valeur donnée, cela te donnerait les mêmes caractéristiques d'une installation sur l'autre, à moins de changer l'ordre d'utilisation des RANDOM ou d'introduire de nouveaux tirages aléatoires entre deux.

A noter qu'il existe aussi PATCH_RANDOM_SEED, de sorte que tu pourrais te passer du INNER_ACTION_BEGIN. Mais manifestement le générateur aléatoire doit être unique et utiliser l'une ou l'autre des commandes devrait avoir le même effet.

Posté : jeu. 23 juil. 2015, 23:47
par Freddy_Gwendo
Je n'ai pas trouvé d'autre RANDOM_SEED dans mes tp...

Cependant, bonne nouvelle, je viens de refaire 6 installations de 10 créatures et ça a fonctionné : des caractéristiques différentes pour chaque créature et différentes à chaque installation.

Va comprendre !

Par ailleurs, j'ai hérité d'un code RANDOM_SEED de mike1072 auquel je ne comprends pas grand chose :

Code : Tout sélectionner

// initializes the random number generator for the creature using the resource name as the default seed
// set quasi_random to a specific string if you want multiple creatures to share the same "random" results
DEFINE_PATCH_FUNCTION ~CRE_INIT~
  STR_VAR
    quasi_random = EVALUATE_BUFFER ~%SOURCE_RES%~
BEGIN
  SET q_r_length = STRING_LENGTH ~%quasi_random%~
  
  PATCH_IF (q_r_length == 0) BEGIN
    PATCH_PRINT ~CRE_INIT: Please specify a valid quasi_random string; exiting function.~
  END
  ELSE BEGIN
    // calculate sum* and xor of all bytes
    SET sum = 0
    SET xor = 0
    SET j = 0
    INNER_PATCH ~%quasi_random%~ BEGIN
      FOR (i = 0; i < q_r_length; i += 1) BEGIN
        READ_BYTE i temp
        SET sum += (temp * (i + 1))
        SET xor ^^ = temp << (j * 8)
        SET j += 1
        SET j = j < 4 ? j : 0
      END
    END
    SET result = (xor + sum)
    
    PATCH_RANDOM_SEED result
  END
END
Si ce n'est qu'il est lancé à chaque création de créature et semble être calculé sur le nom du fichier de la créature pour être certain de générer des valeurs différentes. Mais ce n'est qu'un interprétation...