我比较喜欢玩游戏王决斗链接这个游戏,但作为一名程序员有时也会思考游戏中的功能是如何实现的,因为也想成为一名游戏服务端工程师,这篇文章分享本人对其中用宝石抽卡抽到卡片类型的概率探讨与模拟实现
这个游戏抽到的卡类型有四种,超凡卡,稀有卡,珍贵卡,普通卡,我们让这四种类型的卡片抽到的概率加起来为100%,各为多少当然由策划配置了,开发直接读取配置表就好。既然加起来为100%,那么就用1~100的整数分别代表各种卡片类型的概率的范围,例如:抽到超凡卡的概率为3%,那么概率范围就是:1~3;抽到稀有卡的概率为5%,那么概率范围就是:4~8;抽到珍贵卡的概率为20%,那么概率范围就是:9~28;抽到普通卡的概率为72%,那么概率范围就是:29~100;每抽一张卡的时候就随机产生一个1~100之间的整数,这个整数在哪个范围就是抽到哪种卡了
@Data
public class CardType {
private String type; //卡牌类型
private int probability;//抽卡概率
private int min;//概率范围最小值
private int max;//概率范围最大值
public CardType(String type, int probability) {
this.type = type;
this.probability = probability;
}
}
游戏本身有趣的就是随机性,但是有时为了留住用户也会人为随机,比如:有的玩家运气好的时候有可能抽10次卡就能抽到好几张超凡卡,有的玩家运气不好连抽100次都抽不到超凡那么游戏就可能流失这个玩家了,因此可以人为的干扰概率:例如,1:设置一个超凡卡的概率保底次数,若玩家连续抽卡没有抽到超凡卡的次数累计达到此保底次数,则系统强制玩家获得,之后保底次数重置;2:设置抽卡n次里获得超凡卡的最大次数,比如抽100次卡,但设置超凡卡的保底次数为10,用户则可获得10张超凡卡,感觉用户赚了,若规定抽100次最多获得5张超凡卡,即可不受保底次数的影响了。个人认为规则1一般针对一次抽卡次数较少的用户,规则2一般是针对那些比较土豪的;不管怎样我们做成灵活的方法即可,以下为自己所想的模拟实现:
/**
* 模拟实现游戏王决斗链接中的抽卡概率问题
*/
public class RateRandomNumber {
//使用LinkedHashMap原因,确保概率最小的排在第一(逻辑需要)
public static Map<String,CardType> cardTypes = new LinkedHashMap<>();
/**
* @description: 初始化数据
* @return:
* @author: Ming
* @time: 2022/6/9
*/
static {
cardTypes.put("超凡",new CardType("超凡",3));
cardTypes.put("稀有",new CardType("稀有",5));
cardTypes.put("珍贵",new CardType("珍贵",20));
cardTypes.put("普通",new CardType("普通",72));
setCardProbabilityRang();
}
/**
* @description: 根据概率设置概率范围
* 说明:
* 抽卡就是随机生成1到100之间的整数,因为各种卡类型抽到的概率加起来一共是100%,这里就以1~100的数字表示各种卡类型的概率范围,
* 例如:超凡卡概率%3,则概率范围为1~3,珍贵卡概率%5,则概率范围为4~8,以此类推,最后随机生成的整数在哪个范围就是抽到哪种类型的卡
* @return:
* @author: Ming
* @time: 2022/6/9
*/
public static void setCardProbabilityRang(){
int preMax = 0;//前一个元素概率范围数值最大值
for (Map.Entry<String,CardType> m: cardTypes.entrySet()){
CardType cardType = m.getValue();
int max = cardType.getProbability()+preMax;//当前卡概率范围数值最大值就是:当前卡的概率+前一个元素概率范围数值最大值
cardType.setMax(max);
cardType.setMin(preMax+1);//当前卡概率范围数值最小值就是:前一个元素概率范围数值最大值+1
preMax = max;
}
}
/**
* @description: 抽卡
* @param aGuaranteed 超凡保底次数,若连续n-1次没有抽到超凡,则第n次强制获得
* @param aGetMax 参数times次最多只能抽到多少张超凡卡(限制运气较好的用户)
* @param times 用户抽卡次数
* @return:
* @author: Ming
* @time: 2022/6/9
*/
private static void drawCard(int aGuaranteed, int aGetMax, int times){
Random random = new Random();
CardType a = cardTypes.get("超凡");
CardType b = cardTypes.get("稀有");
CardType c = cardTypes.get("珍贵");
int aGetTimes = 0;
int bGetTimes = 0;
int cGetTimes = 0;
int dGetTimes = 0;
int notTimes = 0; //没有抽中超凡的累计次数
//开始抽卡
for (int i = 0; i < times; i++) {
//随机生成1到100之间的整数,因为各种卡的概率加起来一共是100%,这里就以1~100的数字表示各种卡类型的概率范围,
//例如:超凡概率%3,则概率范围为1~3,珍贵卡概率%5,则概率范围为4~8,以此类推
int num = random.nextInt(100)+1;
if(notTimes == aGuaranteed && aGetTimes < aGetMax){
System.out.println("系统强制发放超凡卡");
aGetTimes++;
notTimes = 0;//重置
}else {
notTimes++;
if(num <= a.getMax() && aGetTimes < aGetMax){
System.out.println("抽中超凡卡");
aGetTimes++;
notTimes = 0;
}else if(num <= b.getMax()){
System.out.println("抽中稀有卡");
bGetTimes++;
}else if(num <= c.getMax()){
System.out.println("抽中珍贵卡");
cGetTimes++;
}else{
System.out.println("抽中普通卡");
dGetTimes++;
}
}
}
System.out.println("本次抽到超凡卡数量:"+aGetTimes);
System.out.println("本次抽到稀有卡数量:"+bGetTimes);
System.out.println("本次抽到珍贵卡数量:"+cGetTimes);
System.out.println("本次抽到普通卡数量:"+dGetTimes);
}
public static void main(String[] args) {
drawCard(20,3,100);
}
}
测试结果
以上就是简单实现,实际开发中,一些基数都是服务器启动时读取配置,数据预先加载到内存的,例如:卡类型的概率,还有卡组等,比如抽到超凡卡,系统就随机从内存分配一张超凡卡id给用户。实际网易实现的概率算法要复杂得多,这里只是作为自己问题的思考,探讨,实现;有错希望大家多多指正