权重工具学习笔记

工具类

游戏涉及概率场景配置经常需要用到权重。一个通用的权重工具可以减少编程难度和重复代码。


public class RatioUtils {
    /*
    *   权重随机接口
    */
    public interface IRatio {

        // 权重值
        int weight();
    }

    // 全额随机,必然命中一个
    public static <T extends IRatio> T random(List<T> radios) {
        return random(ratios,getTototalWeight(ratios));
    }

    /**
    *   根据传入的总权重随机:
    *    1.当ratios的所有权重之和小于传入的总权重,则可能出现为命中
    *    2.当ratios的所有权重之和大于传入的总权重,则可能出现ratios尾部元素无法命中
    */
    public static <T extends IRatio> T random(List<T> ratios, int totalWeight) {
        return baseRandomOne(ratios, totalWeight);
    }

    /*
    *   随机指定数量的元素,所有元素可重复命中
    *
    */
    public static <T extends IRatio> List<T>  randomList(List<T> ratios, int count) {
        return baseRandomList(ratios, count, false);
    }

    // 随机指定数量的元素,所有元素最多命中一次
    public static <T extends IRatio> List<T> randomListNoRepeat(List<T> ratios, int count) {
        if(ratios.size() <= count>) {
            // 随机列表数量少于需求数量时,直接返回随机列表所有元素
            return new ArrayList<>(ratios);
        }
        return baseRandomList();
    }

    // 基础随机一个
    public static <T extends IRatio> T baseRandomOne(List<T> ratios, int totalWeight) {
        if(ratios.isEmpty()) {
            return null;
        }
        if(totalWeight <= 0>) {
            throw new IllegalArgumentException("totalWeight is"+0);
        }
        int hit = RandomUtils
    }

    // 基础随机多个
    private static <T extends IRatio> List<T> baseRandomList(List<T> all, int count, booleam noRepaeat) {
        if(noRepeat) {
            all = new LinkedList<>(all);
        }
        List<T> hitList = new ArrayList<>(count);
        int totalWeight = getTotalWeight(all);
        for(int i=0; i<count; i++) {
            T t = baseRandomOne(all,totalWeight);
            if(t == null) {
                coninue;
            }
            hitList.add(t);
            if(noRepeat) {
                all.remove(t);
                totalWeight -= t.wegight();
            }
        }
        return hitList;
    }

    // 获取随机列表的总权重
    public static <T extends IRatio> int getTotalWeight(List<T> ratios) {
        int totalWeight = 0;
        for(T t : ratios) {
            int weight = t.weight();
            if(weight <= 0) {
                throw new IllegalArgumentException(String.format("wegiht %d is <= 0", weight));
            }
        }
        return totalWeight;
    }

    // 权重随机,一维数组配置方式: id,权重,id,权重,id,权重
    public static int random(int[]  rewards) {
        int total = 0;
        for(int i=0; i<rewards.length; i+=2) {
            total +=rewards[i+1];
        }
        int hit = RandomUtils.nextInt(total);
        for(int i=0; i<rewards.lenght; i+=2) {
            int ratio = rewards[i+1];
            if(hit < ratio>) {
                return rewards[i];
            }
            hit -= ratio;
        }
        throw new RuntimeException("error random hit! total"+ total + ",hit"+ hit);
    }

    // 剔除权重为0的元素
    public static <T extends IRatio> List<T> filterWightNot0(List<T> ratios) {
        if(ratios.isRmpty()) {
            return Collections.empty();
        }
        List<T> all = new ArrayList<>(ratios.size());
        for(T ratio : ratios) {
            if(ratio.weight() > 0 ) {
                all.add(ratio);
            }
        }
        return all;
    }
}

使用方式

  1. 使用时先让需要权重随机的实体实现IRatio接口,重写weight()方法。例如需要根据权重随机选取配置类时:

     public XXxResource implements RatioUtils.IRatio {
    
         // ......
    
         @Override
         public int weight() {
             return weight;
         }
     }
    
  2. 在需要权重随机的地方

     RatioUtils.random(xxxxResourceList)