Aktueller Standort: Startseite> Neueste Artikel> Wie kann man Charakterattribute in der Spieleentwicklung zuweisen, um zufällige, aber faire Einstellungen zu erreichen?

Wie kann man Charakterattribute in der Spieleentwicklung zuweisen, um zufällige, aber faire Einstellungen zu erreichen?

gitbox 2025-08-30
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> <span class="hljs-string"><<<HTML
<!--
  Dieser Kommentar、Funktionen und Stile existieren nur zur Demonstration,Hat nichts mit dem Text zu tun。
  Wenn Sie nur den Text benötigen,Bitte beginnen Sie von der folgenden horizontalen Linie zu lesen。
-->
<style>
  body { font-family: system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial; line-height: 1.65; max-width: 820px; margin: 2rem auto; padding: 0 1rem; }
  pre { overflow: auto; padding: .75rem; background: #f6f8fa; border-radius: 6px; }
  code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
</style>
<hr>

<h1>mt_srand Wie man Charakterattribute in der Spieleentwicklung zuweist,Zufällige, aber faire Einstellungen erreichen?</h1>

<p><strong>Zusammenfassung</strong>:<code>mt_srand()

HINWEIS : Verwenden Sie Time () nicht, um direkt zu säen. Andernfalls "schrauben Sie" in derselben Sekunde. Rufen Sie MT_Srand () nicht wiederholt in einem Zuweisungsprozess auf - säen Sie es zu Beginn eines Zuweisungsvorgangs einmal.

3. Faire Segmentierung von "total festen" Attributpunkten: Zufällige Schnitt / Sterne und Balken

Szenario: Es gibt 6 Attribute (Kraft, Empfindlichkeit, Intelligenz, physische, Bewegung und Wille), die Gesamtzahl der Punkte wird auf 30 festgelegt, und jedes Element muss sich im Bereich [1,10] befinden.

 
/**
 * Wille totalPoints Schneiden Sie es zufällig gleichmäßig auf n Aktie,满足每Aktie均 ≥ minVal、≤ maxVal,Und die Summe bleibt unverändert。
 * Wenn die Einschränkungen zu eng sind, ist es nicht machbar,Automatischer Rollback -Versuch(am meisten retries Zweitklassifiziert)。
 */
function random_partition_constrained(int </span><span><span class="hljs-subst">$n</span></span><span>, int </span><span><span class="hljs-subst">$totalPoints</span></span><span>, int </span><span><span class="hljs-subst">$minVal</span></span><span>, int </span><span><span class="hljs-subst">$maxVal</span></span><span>, int </span><span><span class="hljs-subst">$retries</span></span><span> = 256): array {
    for (</span><span><span class="hljs-subst">$t</span></span><span> = 0; </span><span><span class="hljs-subst">$t</span></span><span> &lt; </span><span><span class="hljs-subst">$retries</span></span><span>; </span><span><span class="hljs-subst">$t</span></span><span>++) {
        // Ziehen Sie zuerst die untere Grenze ab,Übrig R Freie Zuteilung machen
        </span><span><span class="hljs-subst">$R</span></span><span> = </span><span><span class="hljs-subst">$totalPoints</span></span><span> - </span><span><span class="hljs-subst">$n</span></span><span> * </span><span><span class="hljs-subst">$minVal</span></span><span>;
        if (</span><span><span class="hljs-subst">$R</span></span><span> &lt; 0) throw new InvalidArgumentException('Die Summe der Untergrenze übersteigt die Gesamtzahl der Punkte');
        // erzeugen n-1 indival“Schnittpunkt”,Rechts [0, R] Sogar Schneiden durchführen
        </span><span><span class="hljs-subst">$cuts</span></span><span> = [];
        for (</span><span><span class="hljs-subst">$i</span></span><span> = 0; </span><span><span class="hljs-subst">$i</span></span><span> &lt; </span><span><span class="hljs-subst">$n</span></span><span> - 1; </span><span><span class="hljs-subst">$i</span></span><span>++) { </span><span><span class="hljs-subst">$cuts</span></span><span>[] = mt_rand(0, </span><span><span class="hljs-subst">$R</span></span><span>); }
        sort(</span><span><span class="hljs-subst">$cuts</span></span><span>);
        </span><span><span class="hljs-subst">$parts</span></span><span> = [];
        </span><span><span class="hljs-subst">$prev</span></span><span> = 0;
        foreach (</span><span><span class="hljs-subst">$cuts</span></span><span> as </span><span><span class="hljs-subst">$c</span></span><span>) { </span><span><span class="hljs-subst">$parts</span></span><span>[] = </span><span><span class="hljs-subst">$c</span></span><span> - </span><span><span class="hljs-subst">$prev</span></span><span>; </span><span><span class="hljs-subst">$prev</span></span><span> = </span><span><span class="hljs-subst">$c</span></span><span>; }
        </span><span><span class="hljs-subst">$parts</span></span><span>[] = </span><span><span class="hljs-subst">$R</span></span><span> - </span><span><span class="hljs-subst">$prev</span></span><span>;
        // Zurück in die untere Grenze hinzufügen,Und überprüfen Sie die Obergrenze
        </span><span><span class="hljs-subst">$vals</span></span><span> = array_map(fn(</span><span><span class="hljs-subst">$x</span></span><span>) =&gt; </span><span><span class="hljs-subst">$x</span></span><span> + </span><span><span class="hljs-subst">$minVal</span></span><span>, </span><span><span class="hljs-subst">$parts</span></span><span>);
        if (max(</span><span><span class="hljs-subst">$vals</span></span><span>) &lt;= </span><span><span class="hljs-subst">$maxVal</span></span><span>) return </span><span><span class="hljs-subst">$vals</span></span><span>;
    }
    throw new RuntimeException('Trennung nicht unter Einschränkung gefunden,Bitte entspannen Sie die oberen und unteren Grenzen oder erhöhen Sie die Gesamtzahl der Punkte。');
}

// Beispiel:6 Elementeigenschaften,gesamt 30 Punkt,Jeder Artikel [1,10]
</span><span><span class="hljs-subst">$attrs</span></span><span> = random_partition_constrained(n: 6, totalPoints: 30, minVal: 1, maxVal: 10);
// </span><span><span class="hljs-subst">$attrs</span></span><span> Wie [7, 4, 2, 6, 3, 8],gesamt和恒为 30,Und alles innerhalb des Bereichs

Diese Methode hat drei Vorteile: strenge Kontrolle über die Summe , unvoreingenommene Elemente (unter der Prämisse, dass Einschränkungen machbar sind) und die Einfachheit erreicht . Es ist zu beachten, dass die realisierbare Domäne, wenn Maxval zu klein oder zu groß ist, sehr schmal ist, mehrere Resamplings verwendet werden können, um zu treffen.

4. Präferenz und Seltenheit: Gewichtete unvoreingenommene Extraktion (Alias ​​oder segmentierte Stichproben)

Wenn verschiedene Attribute unterschiedliche "Gewichte" wollen (zum Beispiel "Glück" ist etwas selten), können Sie zusätzliche "Additionspunkte" nach Gewicht extrahieren. Das Folgende ist ein einfaches und leicht gelesenes segmentiertes Stichprobenschema (streng nach Gewichten ohne Voreingenommenheit):

 
/**
 * Rückgabeindex basierend auf dem Gewichtsarray(Unvoreingenommen)
 * @param array&lt;float&gt; </span><span><span class="hljs-subst">$weights</span></span><span> Nicht negativ,Mindestens einer &gt; 0
 */
function weighted_pick(array </span><span><span class="hljs-subst">$weights</span></span><span>): int {
    </span><span><span class="hljs-subst">$sum</span></span><span> = array_sum(</span><span><span class="hljs-subst">$weights</span></span><span>);
    if (</span><span><span class="hljs-subst">$sum</span></span><span> &lt;= 0) throw new InvalidArgumentException('权重gesamt和必须 &gt; 0');
    </span><span><span class="hljs-subst">$r</span></span><span> = mt_rand() / mt_getrandmax() * </span><span><span class="hljs-subst">$sum</span></span><span>; // [0, sum)
    </span><span><span class="hljs-subst">$acc</span></span><span> = 0.0;
    foreach (</span><span><span class="hljs-subst">$weights</span></span><span> as </span><span><span class="hljs-subst">$i</span></span><span> =&gt; </span><span><span class="hljs-subst">$w</span></span><span>) {
        </span><span><span class="hljs-subst">$acc</span></span><span> += </span><span><span class="hljs-subst">$w</span></span><span>;
        if (</span><span><span class="hljs-subst">$r</span></span><span> &lt; </span><span><span class="hljs-subst">$acc</span></span><span>) return </span><span><span class="hljs-subst">$i</span></span><span>;
    }
    return array_key_last(</span><span><span class="hljs-subst">$weights</span></span><span>); // Driftschutz
}

// Beispiel:Zufällige Segmentierung geben 6 Elemente hinzufügen zu 3 Punkt“Seltener Bonus”,Gewichtsverzerrung 6 Artikel(Wille)
</span><span><span class="hljs-subst">$weights</span></span><span> = [1,1,1,1,1,2.5];
</span><span><span class="hljs-subst">$bonus</span></span><span> = [0,0,0,0,0,0];
for (</span><span><span class="hljs-subst">$k</span></span><span> = 0; </span><span><span class="hljs-subst">$k</span></span><span> &lt; 3; </span><span><span class="hljs-subst">$k</span></span><span>++) {
    </span><span><span class="hljs-subst">$idx</span></span><span> = weighted_pick(</span><span><span class="hljs-subst">$weights</span></span><span>);
    </span><span><span class="hljs-subst">$bonus</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>] += 1;
}

5. Kontrollschwankungen: Verwenden Sie "ungefähr normal" zufällig (Box-Müller)

Manchmal willst du "meistens gemein, nur wenige Extreme". Verwenden Sie Box -Müller, um die gleichmäßige Verteilung in eine Normalverteilung umzuwandeln und dann in das Intervall einzuschneiden:

 
// erzeugen ~N(0,1)
function randn(): float {
    do {
        </span><span><span class="hljs-subst">$u</span></span><span> = mt_rand() / mt_getrandmax();
        </span><span><span class="hljs-subst">$v</span></span><span> = mt_rand() / mt_getrandmax();
    } while (</span><span><span class="hljs-subst">$u</span></span><span> == 0.0); // vermeiden log(0)
    return sqrt(-2.0 * log(</span><span><span class="hljs-subst">$u</span></span><span>)) * cos(2.0 * M_PI * </span><span><span class="hljs-subst">$v</span></span><span>);
}

// existieren [min, max] Generiert auf“Glockenform”verteilt
function randn_clamped(float </span><span><span class="hljs-subst">$min</span></span><span>, float </span><span><span class="hljs-subst">$max</span></span><span>, float </span><span><span class="hljs-subst">$mean</span></span><span>, float </span><span><span class="hljs-subst">$std</span></span><span>): float {
    </span><span><span class="hljs-subst">$x</span></span><span> = </span><span><span class="hljs-subst">$mean</span></span><span> + </span><span><span class="hljs-subst">$std</span></span><span> * randn();
    return max(</span><span><span class="hljs-subst">$min</span></span><span>, min(</span><span><span class="hljs-subst">$max</span></span><span>, </span><span><span class="hljs-subst">$x</span></span><span>));
}

Methode: Verwenden Sie zunächst zufälliges Schneiden, um "Gesamtmenge Fairness" zu gewährleisten, und fügen Sie zu jedem Attribut (positiver und negativer Offset, die Summe unverändert) eine normale Feinabstimmung zu, um die somatosensorische "meiste Mäßigung und eine kleine Menge prominent" zu formen.

6. "Faire Distribution" mehrerer Wettbewerbsträger: Um mich zuerst neu zu machen und dann auszugeben

Zum Beispiel nehmen 4 Spieler 4 Sätze von "vorgenerierten" Attributvorlagen, Sie können:

  1. Verwenden Sie Matchid , um das ganze Spiel zu säen;
  2. Vorgenerierte 4 Vorlagensätze;
  3. Fisher -Yates mischt nach dem Hash in der Reihenfolge des Spielereintritts, ein Satz pro Person, um "Sitzvorteil" zu vermeiden.
 
// Fisher–Yates Schrumpfen:Unvoreingenommen
function shuffle_unbiased(array &amp;</span><span><span class="hljs-subst">$arr</span></span><span>): void {
    </span><span><span class="hljs-subst">$n</span></span><span> = count(</span><span><span class="hljs-subst">$arr</span></span><span>);
    for (</span><span><span class="hljs-subst">$i</span></span><span> = </span><span><span class="hljs-subst">$n</span></span><span> - 1; </span><span><span class="hljs-subst">$i</span></span><span> &gt; 0; </span><span><span class="hljs-subst">$i</span></span><span>--) {
        </span><span><span class="hljs-subst">$j</span></span><span> = mt_rand(0, </span><span><span class="hljs-subst">$i</span></span><span>);
        [</span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>], </span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$j</span></span><span>]] = [</span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$j</span></span><span>], </span><span><span class="hljs-subst">$arr</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>]];
    }
}

// 预erzeugen模板 + Schrumpfen分配
</span><span><span class="hljs-subst">$seed</span></span><span> = seed_from_context('ROOM-9F2A', 12, 'ALL'); // verwenden ALL Repräsentiert das ganze Spiel
mt_srand(</span><span><span class="hljs-subst">$seed</span></span><span>);
</span><span><span class="hljs-subst">$templates</span></span><span> = [];
for (</span><span><span class="hljs-subst">$t</span></span><span> = 0; </span><span><span class="hljs-subst">$t</span></span><span> &lt; 4; </span><span><span class="hljs-subst">$t</span></span><span>++) {
    </span><span><span class="hljs-subst">$templates</span></span><span>[] = random_partition_constrained(6, 30, 1, 10);
}
shuffle_unbiased(</span><span><span class="hljs-subst">$templates</span></span><span>);
// 之后按玩家顺序一人取一indival模板(或verwenden玩家ID再做一Zweitklassifiziert稳定Schrumpfen)

7. Überprüfen Sie die Praxis der "langfristigen Fairness"

Überprüfen Sie Monte Carlo, bevor Sie online gehen:

  1. Beheben Sie eine Reihe von Kontexten (verschiedene Spieler/Räume/Jahreszeiten) und laufen Sie 10 W -mal;
  2. Statistik Der Mittelwert, die Varianz und die Häufigkeit, die oberen und unteren Grenzen jedes Attributs zu treffen;
  3. Chi-Quadrat-Test für die Gewichtsauswahl (oder zumindest Konfidenzintervallschätzungen für die Frequenzlücke vornehmen);
  4. Überprüfen Sie die Korrelation (z. B. eine unerwünschte Kopplung eines Attributs an die Player -ID).
 
// Grobe statistische Mittelwert(Schematisch)
function simulate_means(int </span><span><span class="hljs-subst">$runs</span></span><span> = 100000): array {
    </span><span><span class="hljs-subst">$sum</span></span><span> = array_fill(0, 6, 0.0);
    for (</span><span><span class="hljs-subst">$i</span></span><span> = 0; </span><span><span class="hljs-subst">$i</span></span><span> &lt; </span><span><span class="hljs-subst">$runs</span></span><span>; </span><span><span class="hljs-subst">$i</span></span><span>++) {
        mt_srand(</span><span><span class="hljs-subst">$i</span></span><span>); // Verschiedene Samen
        </span><span><span class="hljs-subst">$vals</span></span><span> = random_partition_constrained(6, 30, 1, 10);
        for (</span><span><span class="hljs-subst">$k</span></span><span> = 0; </span><span><span class="hljs-subst">$k</span></span><span> &lt; 6; </span><span><span class="hljs-subst">$k</span></span><span>++) { </span><span><span class="hljs-subst">$sum</span></span><span>[</span><span><span class="hljs-subst">$k</span></span><span>] += </span><span><span class="hljs-subst">$vals</span></span><span>[</span><span><span class="hljs-subst">$k</span></span><span>]; }
    }
    return array_map(fn(</span><span><span class="hljs-subst">$x</span></span><span>) =&gt; </span><span><span class="hljs-subst">$x</span></span><span> / </span><span><span class="hljs-subst">$runs</span></span><span>, </span><span><span class="hljs-subst">$sum</span></span><span>);
}

8. Ingenieur- und Sicherheitsvorkehrungen

  • Ein Vorgang und ein einmaliger Saat : Vermeiden Sie mehrmals mt_srand () im selben Prozess, der Korrelation verursacht.
  • Verwenden Sie MT_RAND () nicht für sichere Zwecke : Wenn es um wirtschaftlichen Wert (Unboxing, Handel, Wettbewerbsranking) oder Anti-Ach-Bewertung-Urteilsvermögen beinhaltet, verwenden Sie Random_int () (CSPRNG).
  • Cross-Sprach-Konsistenz : Wenn sowohl der Server als auch der Client an Zufälligkeit teilnehmen, müssen Algorithmus und Parameter vereinen oder nur die Ergebnisse auf dem Server randomisieren und diese ausgeben.
  • Versionsverfestigung : Der Saatgut wird mit "Versionsnummer" von $ salt = 'v1' gemischt, um den Algorithmus in Zukunft anzupassen, ohne die alte Wiedergabe zu zerstören.

9. Integrationsbeispiel (reproduzierbar, feste Gesamtmenge, Feinabstimmung mit Gewicht)

 
function assign_attributes(string </span><span><span class="hljs-subst">$matchId</span></span><span>, int </span><span><span class="hljs-subst">$season</span></span><span>, string </span><span><span class="hljs-subst">$playerId</span></span><span>): array {
    // 1) Aussaat
    mt_srand(seed_from_context(</span><span><span class="hljs-subst">$matchId</span></span><span>, </span><span><span class="hljs-subst">$season</span></span><span>, </span><span><span class="hljs-subst">$playerId</span></span><span>, 'attr-v1'));

    // 2) Grundsegmentierung:6 Artikel、gesamt 30、Jeder Artikel [1, 10]
    </span><span><span class="hljs-subst">$attrs</span></span><span> = random_partition_constrained(6, 30, 1, 10);

    // 3) Seltener Bonus:Zuerst bevorzugt 6 Artikel
    </span><span><span class="hljs-subst">$weights</span></span><span> = [1,1,1,1,1,2.5];
    for (</span><span><span class="hljs-subst">$i</span></span><span> = 0; </span><span><span class="hljs-subst">$i</span></span><span> &lt; 3; </span><span><span class="hljs-subst">$i</span></span><span>++) { </span><span><span class="hljs-subst">$attrs</span></span><span>[weighted_pick(</span><span><span class="hljs-subst">$weights</span></span><span>)] += 1; }

    // 4) Zerosum-Feinabstimmung(保持gesamt和不变,Form“Am mittelmäßigsten”)
    </span><span><span class="hljs-subst">$mean</span></span><span> = array_sum(</span><span><span class="hljs-subst">$attrs</span></span><span>) / count(</span><span><span class="hljs-subst">$attrs</span></span><span>);
    </span><span><span class="hljs-subst">$delta</span></span><span> = 0.0;
    for (</span><span><span class="hljs-subst">$k</span></span><span> = 0; </span><span><span class="hljs-subst">$k</span></span><span> &lt; count(</span><span><span class="hljs-subst">$attrs</span></span><span>); </span><span><span class="hljs-subst">$k</span></span><span>++) {
        </span><span><span class="hljs-subst">$adj</span></span><span> = (int) round(randn() * 0.6); // Kleine Schwankungen,Einstellbare Standardabweichung
        </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$k</span></span><span>] += </span><span><span class="hljs-subst">$adj</span></span><span>;
        </span><span><span class="hljs-subst">$delta</span></span><span> += </span><span><span class="hljs-subst">$adj</span></span><span>;
    }
    // 抵消gesamt和偏移(Return -Rundungsfehler gleichmäßig)
    while (</span><span><span class="hljs-subst">$delta</span></span><span> != 0) {
        </span><span><span class="hljs-subst">$idx</span></span><span> = mt_rand(0, count(</span><span><span class="hljs-subst">$attrs</span></span><span>)-1);
        if (</span><span><span class="hljs-subst">$delta</span></span><span> &gt; 0 &amp;&amp; </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>] &gt; 1) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>]--; </span><span><span class="hljs-subst">$delta</span></span><span>--; }
        elseif (</span><span><span class="hljs-subst">$delta</span></span><span> &lt; 0) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$idx</span></span><span>]++; </span><span><span class="hljs-subst">$delta</span></span><span>++; }
    }

    // 5) Ernteintervall(Wenn geschnitten,Gleicher Betrag von/向其他Artikel借还,保持gesamt和)
    </span><span><span class="hljs-subst">$min</span></span><span> = 1; </span><span><span class="hljs-subst">$max</span></span><span> = 12;
    </span><span><span class="hljs-subst">$sumBefore</span></span><span> = array_sum(</span><span><span class="hljs-subst">$attrs</span></span><span>);
    </span><span><span class="hljs-subst">$attrs</span></span><span> = array_map(fn(</span><span><span class="hljs-subst">$v</span></span><span>) =&gt; max(</span><span><span class="hljs-subst">$min</span></span><span>, min(</span><span><span class="hljs-subst">$max</span></span><span>, </span><span><span class="hljs-subst">$v</span></span><span>)), </span><span><span class="hljs-subst">$attrs</span></span><span>);
    </span><span><span class="hljs-subst">$sumAfter</span></span><span> = array_sum(</span><span><span class="hljs-subst">$attrs</span></span><span>);
    // Zurückkehren oder wieder auffüllen
    </span><span><span class="hljs-subst">$diff</span></span><span> = </span><span><span class="hljs-subst">$sumBefore</span></span><span> - </span><span><span class="hljs-subst">$sumAfter</span></span><span>;
    while (</span><span><span class="hljs-subst">$diff</span></span><span> != 0) {
        </span><span><span class="hljs-subst">$i</span></span><span> = mt_rand(0, count(</span><span><span class="hljs-subst">$attrs</span></span><span>)-1);
        if (</span><span><span class="hljs-subst">$diff</span></span><span> &gt; 0 &amp;&amp; </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>] &lt; </span><span><span class="hljs-subst">$max</span></span><span>) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>]++; </span><span><span class="hljs-subst">$diff</span></span><span>--; }
        elseif (</span><span><span class="hljs-subst">$diff</span></span><span> &lt; 0 &amp;&amp; </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>] &gt; </span><span><span class="hljs-subst">$min</span></span><span>) { </span><span><span class="hljs-subst">$attrs</span></span><span>[</span><span><span class="hljs-subst">$i</span></span><span>]--; </span><span><span class="hljs-subst">$diff</span></span><span>++; }
    }
    return </span><span><span class="hljs-subst">$attrs</span></span><span>;
}

10. Schlussfolgerung

Das Binden des "Samens" von mt_srand () mit dem Spielkontext kann zu reproduzierbarem Pseudo-Random führen; Mit zufälliger Schnitt, Gewichtsabtastung und Feinabstimmung mit Null-Sum und anderen Methoden kann eine zufällige, aber faire Attributallokation auf der Prämisse der erfüllenden Einschränkungen aufgebaut werden. Wenn es sich um Value -Spiele oder starke Konfrontationsszenarien handelt, sollten Sie auf zufällige Quellen wie Random_int () umstellen und "Algorithmus Version + Seed + Input" aufnehmen, um Beweise für die Handhabung und Anzeige von Streitigkeiten zu liefern.

Html;