com.neko233.toolchain.game.draw.WeightDrawCardBox Maven / Gradle / Ivy
package com.neko233.toolchain.game.draw;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* 权重抽卡盒子
*
* @param 抽取的卡
*/
public class WeightDrawCardBox implements Serializable, Cloneable {
private final Map weightCardMap;
private final int totalWeight;
public WeightDrawCardBox(Map weightCardMap) {
this(weightCardMap, weightCardMap.values().stream().mapToInt(i -> i).sum());
}
private WeightDrawCardBox(Map weightCardMap, int totalWeight) {
this.weightCardMap = weightCardMap;
this.totalWeight = totalWeight;
assert !isEmpty();
}
public WeightDrawCardBox clone() {
return new WeightDrawCardBox<>(this.weightCardMap);
}
public CARD randomInRepeat() {
Map tempMap = this.weightCardMap;
if (tempMap.isEmpty()) {
return null;
}
float index = (ThreadLocalRandom.current().nextFloat() * this.totalWeight);
for (Map.Entry entry : tempMap.entrySet()) {
index -= entry.getValue();
if (index < 0) {
return entry.getKey();
}
}
throw new RuntimeException("WeightDrawCardBox totalWeight has gone crazy! currentIndex:" + index);
}
public CARD randomOne() {
return randomInRepeat();
}
public List randomInMutex(int times, Function extraHandleFunction) {
if (times <= 0 || times > this.weightCardMap.size()) {
throw new RuntimeException("WeightDrawCardBox randomRepeatedlyMutex not enough currentSize=" + this.weightCardMap.size() + ",require times=" + times);
}
int totalWeight = this.totalWeight;
Set result = new HashSet<>(times);
while (result.size() < times) {
int index = (int) (ThreadLocalRandom.current().nextDouble() * totalWeight);
for (Map.Entry entry : this.weightCardMap.entrySet()) {
index -= entry.getValue();
if (index <= 0) {
CARD key = entry.getKey();
if (result.contains(key)) {
continue;
}
if (extraHandleFunction != null) {
result.add(extraHandleFunction.apply(key));
break;
}
result.add(key);
break;
}
}
}
return new ArrayList<>(result);
}
public List randomRepeatedly(int times) {
List result = new ArrayList<>(times);
for (int i = 0; i < times; i++) {
result.add(randomInRepeat());
}
return result;
}
public List randomInMutex(int times) {
return randomInMutex(times, null);
}
/**
* 过滤条件下,返回一个新的盒子
*
* @return 新的盒子,内部都是符合条件的卡牌
*/
public WeightDrawCardBox generateNewBox(Predicate filter) {
return this.filter(filter).orElse(null);
}
/**
* 返回新的 RandomBox。若新的 RandomBox 内元素为空,则返回 Option.empty()
*
* @param filterFunc 过滤
* @return 可能为空的新盒子
*/
public Optional> filter(Predicate filterFunc) {
Map weightCardMap = new HashMap<>(this.weightCardMap.size(), 1.0f);
int totalWeight = 0;
for (Map.Entry entry : this.weightCardMap.entrySet()) {
CARD key = entry.getKey();
if (filterFunc == null || filterFunc.test(key)) {
continue;
}
weightCardMap.put(entry.getKey(), entry.getValue());
totalWeight += entry.getValue();
}
if (totalWeight == 0 || this.weightCardMap.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(new WeightDrawCardBox<>(weightCardMap, totalWeight));
}
}
public Integer getWeight(CARD card) {
return this.weightCardMap.get(card);
}
public boolean isEmpty() {
return totalWeight <= 0 || weightCardMap.isEmpty();
}
public int getCardCount() {
return weightCardMap.size();
}
public Set getKeys() {
return weightCardMap.keySet();
}
}