package l2p.commons.collections.chance;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.math.BigDecimal;
import l2p.commons.util.Rnd;
/**
* @author Gaikotsu
*/
public class ChanceList<E> implements Iterable<E>
{
public static final int MAX_CHANCE = 100_000_000; // Значение, эквивалентное шансу в 100%
public static final int PER_FACTOR = MAX_CHANCE / 100; // Значение, эквивалентное шансу в 1%
public static final int SCALE = 6; // На 10 в какой степени умножать значение шанса в варианте с получением его из строки через тип BigDecimal
private final List<ChanceNode<E>> _nodes;
private int _totalChance = 0;
/**
* Создает пустой список с размером начального списка элементов по умолчанию
*/
public ChanceList()
{
_nodes = new ArrayList<>();
}
/**
* Создает пустой список с заданным начальным размером списка элементов
*
* @param capacity - размер
*/
public ChanceList(int capacity)
{
_nodes = new ArrayList<>(capacity);
}
/**
* Добавляет новый элемент в список с заданным шансом
*
* @param value - добавляемый элемент
* @param chance - шанс получения этого элемента
*/
public boolean add(E value, int chance)
{
if (value == null || chance <= 0)
return false;
_totalChance += chance;
return _nodes.add(new ChanceNode<>(value, chance));
}
/**
* Добавляет новый элемент в список с заданным шансом
*
* @param value - добавляемый элемент
* @param chance - шанс получения этого элемента в процентах
*/
public boolean add(E value, double chance)
{
return add(value, (int) (chance * PER_FACTOR));
}
/**
* Добавляет новый элемент в список с заданным шансом
*
* @param value - добавляемый элемент
* @param chance - шанс получения этого элемента в процентах
*/
public boolean add(E value, String chance)
{
return add(value, new BigDecimal(chance).scaleByPowerOfTen(SCALE).intValue());
}
/**
* Удаляет заданный элемент из списка
*
* @param value - удаляемый элемент
*/
public boolean remove(E value)
{
if (_nodes.removeIf(node -> node.getValue().equals(value)))
{
calcTotalChance();
return true;
}
return true;
}
/**
* Возвращает элемент из списка по его индексу
*/
public E get(int index)
{
return index >= 0 && index < _nodes.size() ? _nodes.get(index).getValue() : null;
}
/**
* Возвращает случайный элемент из списка (вероятность получения зависит от его шанса)
*/
public E get()
{
int chance = Rnd.get(1, _totalChance);
int current = 0;
for (ChanceNode<E> node : _nodes)
{
current += node.getChance();
if (current >= chance)
return node.getValue();
}
return null;
}
/**
* Возвращает все элементы из списка
*/
public List<E> getAll()
{
return _nodes.stream().map(node -> node.getValue()).collect(Collectors.toUnmodifiableList());
}
/**
* Возвращает суммарный шанс всех элементов из списка
*/
public int getTotalChance()
{
return _totalChance;
}
/**
* Подсчитывает суммарный шанс всех элементов в списке
*/
public void calcTotalChance()
{
_totalChance = _nodes.stream().mapToInt(node -> node.getChance()).sum();
}
/**
* Возвращает суммарный шанс всех элементов из списка в виде процента
*/
public double getCurrentChance()
{
return _totalChance / (double) PER_FACTOR;
}
/**
* Проверяет, равен ли суммарный шанс значению, эквивалентному шансу в 100%
*/
public boolean isValid()
{
return _totalChance == MAX_CHANCE;
}
public boolean isEmpty()
{
return _nodes.isEmpty();
}
public int size()
{
return _nodes.size();
}
public void clear()
{
_totalChance = 0;
_nodes.clear();
}
@Override
public Iterator<E> iterator()
{
return getAll().iterator();
}
}