Показать сообщение отдельно
Старый 22.05.2011, 00:49
ShadowsInRain вне форума Посмотреть профиль Отправить личное сообщение для ShadowsInRain Найти все сообщения от ShadowsInRain
  № 8  
Ответить с цитированием
ShadowsInRain
 
Аватар для ShadowsInRain

Регистрация: Mar 2011
Сообщений: 35
Алгоритм древний и проверенный, но непосредственно АС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.
Так же легко работать и с целочисленными шансами, но я не думаю, что это удобно.


Последний раз редактировалось ShadowsInRain; 22.05.2011 в 01:24. Причина: UPD