Алгоритм древний и проверенный, но непосредственно АС3-класс написан на коленке (NP++) за 5 минут (в АС3 не использовал ибо), так что, возможно, придётся внести фикссы, чтобы компилировалось. Без оптимизации работает не самым быстрым способом. Помимо того, возможно накопление ошибок при сложении чисел с плавающей точкой. При таком раскладе первая зарегистрированная грань будет выпадать гипотетически чаще. Чтобы этого избежать, шансы граней должны быть одного или близких порядков. Грубо говоря, самый маленький шанс и самый большой не должны отличаться более чем на три-четыре порядка. Но в большинстве задач можно забить на эту ошибку, она реально ничтожна.

Код AS3:
package
{
public class VirtualDice // виртуальная кость (ваш К.О.)
{
protected var _chances:Vector.<Number> = new Vector.<Number>; // стороны кости
protected var _faces:Vector.<Number> = new Vector.<Number>; // абсолютные шансы для каждой стороны
protected var _range:Number = 0; // кеш суммы всех шансов, используется при кидании кости
public function getFaceChance(face:Number):Number // текущий вес для грани кости
{
var index:uint = _faces.lastIndexOf(face); // найти грань в наборе
return (index == -1) ? 0 : _chances[index]; // вернуть шанс найденной грани, либо нуль, если не найдена
}
public function setFaceChance(face:Number, chance:Number = 1):void // установить вес грани
{
if(chance < 0) // отрицательные шансы невозможны
chance = 0; // особо дотошные пишут throw new Error("Dice face chance negative value assigned");
var index:uint = _faces.lastIndexOf(face); // найти грань в наборе
if (index == -1) // а нету такой грани в наборе, придётся добавить
{
if(chance == 0) // её и так нет, нет смысла ставить нулевой шанс
return;
_faces.push(face);
_chances.push(chance);
index = _faces.lenght - 1;
}
else
if(chance == 0) // грань с занулённым шансом удалим для экономии памяти
{
_faces.splice(index,1);
_chances.splice(index,1);
}
_range = 0; // нужно обновить кеш
for each (var intermediate:Number in _chances)
result += intermediate;
}
public function roll():Number
{
if(!_faces.lenght) // опа, граней то нету!
rhrow new Error("Rolling dice without faces"); // сферическая кость в ваккууме никогда бы не остановилась
var fortune_charge:Number = _range * Math.random; // Holy Random is True God and only
var lucker:Number = _faces.lenght - 1;
while(lucker)
{
fortune_charge -= _chances[lucker];
if(fortune_charge <= 0.0001)
break;
lucker--;
}
return _faces[lucker];
}
}
}
Если нужно работать только с целочисленными гранями, достаточно заменить только тип для
_face и
faces.
Так же легко работать и с целочисленными шансами, но я не думаю, что это удобно.