четверг, 18 апреля 2013 г.

Случайная выборка с учетом веса

Один из распространенных приемов в игровой логике - использование элемента случайности. Он используется, например, при машинной генерации уровней, лабиринтов, или когда на локации появляются случайные враги или бонусы. При этом зачастую требуется выбрать случайный объект из какого-либо списка с учетом того, что некоторые из них имеют большую вероятность выбора, чем другие - то есть, имеют больший "вес".

Я написал особую реализацию алгоритма такой выборки - она работает не с обычными массивами, а с перечислениями (enum). В шаблон функции передаются два перечисления - самих элементов и их весов. Этот пример иллюстрирует также богатые возможности метапрограммирования, CTFE и интроспекции в D.

import std.stdio;
import std.traits;
import std.random;
import std.algorithm;

T weightedRandomEnum(T, W)()
    if (isNumeric!W &&
        is(T == enum) && is(W == enum) && 
        EnumMembers!T.length == EnumMembers!W.length)
{
    enum members = [EnumMembers!T];
    enum weights = [EnumMembers!W];
    enum weightsSum = reduce!("a + b")([EnumMembers!W]);
    
    auto randomNumber = uniform(0, weightsSum);
    
    foreach(i, weight; weights)
    {
        if (randomNumber < weight)
            return members[i];
        else
            randomNumber -= weight;
    }
    
    assert(0, "Should never get here");
}

enum Color
{
    Red, Yellow, Green, Blue
}

enum Weights
{
    Red = 100, 
    Yellow = 20, 
    Green = 20, 
    Blue = 5
}

void main()
{
    foreach(i; 0..10)
        writeln(weightedRandomEnum!(Color, Weights));
}

Комментариев нет:

Отправить комментарий