Spads -- WangXP

游戏对象(上阵)缘分触发条件表达式解析

本文分为 6 个部分

---------- ---------- ---------- ----------

1、简要

2、应用背景

3、问题提出

4、解决方案

5、测试程序

6、测试结果

---------- ---------- ---------- ----------
1.简要
 对问题透彻分析,能够帮助我们快速、高效的解决它。下文中引用的名词简称会做相应的说明。
 如:游戏对象 -> GameObject

2.应用背景
 当某个游戏对象被放到战斗阵容上时, 因其自身的缘分Buff(也可称之为 属性)会对其他阵中的游
戏对象的某些属性值提供加成。此举目的希望能够提供玩家新的游戏方式。通过对阵容上的游戏对象进
行不同方式的组合达到调整阵容战斗属性以应对不同的对手。
例如:
阵容中现有:[GameObjct1, GameObjct40]

GameObjct1 = {
    "itemId": 1,
    "health": 530,
    "attack": 73
    ...
}
GameObjct40 = {
    "itemId": 40,
    "health": 335,
    "attack": 90
    ...
}


触发条件定义:

<config>
    <buff itemId="1" targetId="12_40" effectProp="health" value="10"/>
    <buff itemId="23" targetId="1" effectProp="attack" value="15"/>
    ...
    <buff .../>
</config>


综上定义, 当游戏对象 23 上阵时,若阵中已存在 buff 目标 1 则会导致游戏对象 1 的某些战斗属
性值有一定幅度的增加(attack), 其具体增加方式此处就不再熬述。

GameObjct23 = {
    "itemId": 23,
    "health": 700,
    "attack": 60
    ...
}


阵容:[GameObjct1, GameObjct40, GameObjct23]
此时的 GameObject1 的 attack 属性值将发生变化。如下:

GameObjct1 = {
    "itemId": 1,
    "health": 530,
    "attack": 88
    ...
}

反之,阵中不存在目标游戏对象时此 buff 将不会触发任何游戏对象的属性发生变化。

大家可能发现 targetId 有这样的 "12_40"格式, 其指的含义是。 阵容中必须同时出现游戏对象 12
与 40 时此缘分才能触发。


3.问题提出
 为了应对游戏新版某些功能;上述缘分触发方式已不满足功能需求。游戏对象在新版功能中能够繁殖
同类型新的游戏对象, 其缘分 Buff 定义继续沿用老游戏对象的缘分。
例如:
GameObject1 繁殖出新的游戏对象: GameObject101
上述定义的触发条件定义中的 itemId="23" 应修改为

<buff itemId="23" targetId="1#101" effectProp="attack" value="15"/>
...

如果其他的缘分触发表达式原先是这样 "12_40" 时, 则新的表达式就会是 "12_40#101"。
那么,问题来了。下面的解决方案就是针对此问题中的表达式进行解析。


4.解决方案

// 常量定义
    private static final String ONE = "1";
    private static final String ZERO = "0";
    private static final String OR = "#";
    private static final String AND = "_";
                                
      /***
       * <b>判定缘分触发</b><br/>
       * 对指定条件表达式与集中中元素的关系进行匹配。其核心算法阐述如下:<br/>
       * 首先,对表达式字符串解析成字符串数组,根据表达式关系运算符优先级关系,将加入队列的数值进行排序已便后期计算。<br/>
       * 在后期计算中有两种, 分为<code>#</code>逻辑或运算和<code>_</code>逻辑与运算。优先级关系是:
       * <pre>
       *    # 高于 _
       * </pre>
       * 当计算过程中从队列中获取的值若非运算符则将其以链表结构保存,否则判定运算符以对应的逻辑或 或者 逻辑与对链表中的数据进行计算。<br/>
       * 并将结果保存在栈中。对于某些特定格式的表达式,在进行与逻辑运算时会从链表中获取数据并计算结果。<br/>
       * 已通过测试的表达式格式如下所示:
       * <pre>
       *    "",
       *    "10",
       *    "56",
       *    "210_10",
       *    "10_210",
       *    "210_99",
       *    "41_23",
       *    "41#210_56",
       *    "210#41_56",
       *    "188#210_41",
       *    "41#210_56#78_23",
       *    "41#210_56#78#99",
       *    "41#210",
       *    "41_56",
       *    "41_2_78_10_23",
       *    "56#78_41_99",
       *    "41_99_56#78",
       *    "99#210#56#77#41",
       *    "41_56#78_99"
       * </pre>
       * 若以条件表达式 2_10#78 , 匹配集合 [2, 10, 78, 41, 23], 那么得到的接触来的运算队列应该是
       * 1, 1, 1, 2, #, 2, _,
       * 结果: true
       * @see Util
       * @param exp 条件表达式
       * @param assemble 集合
       * @return true: 符合缘分触发, false: 不符合
       * @exception null
       */
    static public boolean matchFates(String expression, List<Integer> assemble)
    {
        if (expression == null || expression.length() == 0)
            return false;
        int length = expression.length();
        Queue<String> result = new LinkedList<String>();
        List<String> tempResult = new LinkedList<String>();
        StringBuilder builder = new StringBuilder();
        boolean isNum = Character.isDigit(expression.charAt(0));
        int enableEvaCount = 0;
        boolean cOrExist = false;
        boolean cContinuFlag = false;
        int cOrAppearCount = 0;
        char c;
        char lastC = 'o';
        byte cAndCount = 0;
        for (int charIndex = -1; ++charIndex != length; )
        {
            c = expression.charAt(charIndex);
            if (isNum ^ Character.isDigit(c)) {
                if (builder.length() != 0)
                    tempResult.add(builder.toString());
                if (!Character.isDigit(c)) {
                    if (lastC != 'o' && lastC == c)
                        cContinuFlag = true;
                    else cContinuFlag = false;
                    if (c == '_') {
                        if (lastC != 'o' && lastC == '#') {
                            if (tempResult.size() > 0) {
                                Integer intValue = Integer.valueOf(tempResult.get(tempResult.size() - 1));
                                if (assemble.contains(intValue)) result.add(ONE);
                                else result.add(ZERO);
                                tempResult.remove(tempResult.size() - 1);
                                ++enableEvaCount;
                            }
                            if (((++ cOrAppearCount + 1) <= enableEvaCount) && cContinuFlag)
                                enableEvaCount = cOrAppearCount + 1;
                            result.add(String.valueOf(enableEvaCount));
                            enableEvaCount = 0;
                            result.add(OR);
                            lastC = 'o';
                        }
                        else if (lastC == 'o') {
                            if (tempResult.size() > 0) {
                                Integer intValue = Integer.valueOf(tempResult.get(tempResult.size() - 1));
                                if (assemble.contains(intValue)) result.add(ONE);
                                else result.add(ZERO);
                                tempResult.remove(tempResult.size() - 1);
                                ++enableEvaCount;
                            }
                        }
                        if (++cAndCount == 2) {
                            if (tempResult.size() > 0) {
                                Integer intValue = Integer.valueOf(tempResult.get(tempResult.size() - 1));
                                if (assemble.contains(intValue)) result.add(ONE);
                                else result.add(ZERO);
                                tempResult.remove(tempResult.size() - 1);
                                ++enableEvaCount;
                            }
                            if (cOrExist) {
                                enableEvaCount = cOrAppearCount;
                                cOrAppearCount = 0;
                            }
                            enableEvaCount = enableEvaCount <= 1 ? 2 : enableEvaCount;
                            result.add(String.valueOf(enableEvaCount));
                            enableEvaCount = 0;
                            result.add(AND);
                            -- cAndCount;
                        }
                        lastC = '_';
                    }
                    else if (c == '#') {
                        if (lastC != 'o' && lastC == '_') enableEvaCount = 0;
                        if (tempResult.size() > 0) {
                            Integer intValue = Integer.valueOf(tempResult.get(tempResult.size() - 1));
                            if (assemble.contains(intValue)) result.add(ONE);
                            else result.add(ZERO);
                            tempResult.remove(tempResult.size() - 1);
                            ++enableEvaCount;
                        }
                        lastC = '#';
                        cOrExist = true;
                    }
                }
                else
                {
                    // 数 至 符  || (符 至 数 && (是最后一个数字 || 下个字符不是数字了))
                    if ((isNum && !Character.isDigit(c)) || ((!isNum && Character.isDigit(c))
                                    && (charIndex + 1 == length || !Character.isDigit(expression.charAt(charIndex + 1))
                                )))
                    {
                        if (tempResult.size() != 0)
                        {
                            Integer intValue = Integer.valueOf(tempResult.get(tempResult.size() - 1));
                            if (assemble.contains(intValue)) result.add(ONE);
                            else result.add(ZERO);
                            tempResult.remove(tempResult.size() - 1);
                            ++enableEvaCount;
                        }
                    }
                }
                builder = new StringBuilder();
                isNum = !isNum;
            }
            if (Character.isDigit(c)) builder.append(c);
            if (charIndex + 1 == length)
            {
                // 数
                if (builder.length() != 0) {
                    Integer intValue = Integer.valueOf(builder.toString());
                    if (assemble.contains(intValue)) result.add(ONE);
                    else result.add(ZERO);
                    ++enableEvaCount;
                }
                // #
                if (lastC == '#') {
                    result.add(String.valueOf(enableEvaCount));
                    enableEvaCount = 0;
                    result.add(OR);
                    ++ cOrAppearCount;
                }
                // _
                if (cAndCount == 1) {
                    if (cOrExist)
                        enableEvaCount = 2;
                    else enableEvaCount = enableEvaCount <= 1 ? 2 : enableEvaCount;
                    result.add(String.valueOf(enableEvaCount));
                    enableEvaCount = 0;
                    result.add(AND);
                }
            }
        }
        return eval(result);
    }
    /**
     * 计算队列中值
     * @param oprCodeQue 操作码队列
     * @return 是否触发缘分
     */
    public static boolean eval(Queue<String> oprCodeQue)
    {
        Stack<String> oprStack = new Stack<String>();
        int size = oprCodeQue.size();
        if (size == 1) return oprCodeQue.poll().equals(ONE);
        List<String> nums = new LinkedList<String>();
        int evalNumCount = 0;
        for(String code: oprCodeQue)
        {
            if (ONE.equals(code) || ZERO.equals(code))
            {
                oprStack.add(code);
            }
            else if (OR.equals(code))
            {
                while (evalNumCount-- != 0)
                {
                    nums.add(oprStack.pop());
                }
                if (nums.contains(ONE))
                    oprStack.add(ONE);
                else oprStack.add(ZERO);
                nums.clear();
            }
            else if (AND.equals(code))
            {
                try {
                    while (evalNumCount-- != 0)
                    {
                        nums.add(oprStack.pop());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (nums.contains(ZERO))
                    oprStack.add(ZERO);
                else oprStack.add(ONE);
                nums.clear();
            }
            else
            {
                evalNumCount = Integer.valueOf(code);
            }
        }
        return oprStack.pop().equals(ONE);
    }


5.测试程序

List<Integer> battleDiscipleItems = new ArrayList<Integer>();
        battleDiscipleItems.add(2);
        battleDiscipleItems.add(10);
        battleDiscipleItems.add(78);
        battleDiscipleItems.add(41);
        battleDiscipleItems.add(23);
        List<Integer> requireList = new ArrayList<Integer>();
        Map<Integer, Integer> fatestatus = new HashMap<Integer, Integer>();
        String[] abc = {
                "2_17_93#186_150_19#83#76#188#179_69",
                "2_10#78#99#100",
                "2_10#78",
                "10",
                "56",
                "210_10",
                "10_210",
                "210_99",
                "41_23",
                "41#210_56",
                "210#41_56",
                "188#210_41",
                "41#210_56#78_23",
                "41#210_56#78#99",
                "41#210",
                "41_56",
                "41_2_78_10_23",
                "56#78_41_99",
                "41_99_56#78",
                "99#210#56#77#41",
                "41_56#78_99",
                "41_99_56#78"
        };
        int length = abc.length;
                              
        StringBuilder assBuilder = new StringBuilder();
        for (int j = 0; j < battleDiscipleItems.size(); j++)
        {
            if (j == 0) assBuilder.append("[");
            assBuilder.append(battleDiscipleItems.get(j));
            if (j + 1 == battleDiscipleItems.size())
            {
                assBuilder.append("]\t");
            }
            else {
                assBuilder.append(", ");
            }
        }
                              
        for (int i = 0 ; i < length; i ++)
        {
            Record record = new Record(assBuilder.toString(), abc[i]+ "\t");
            long sTime = System.nanoTime();
            record.setResult(matchFates(abc[i], battleDiscipleItems, record));
            record.setcTime(System.nanoTime(), sTime);
            RecordTimeUtil.inst.put(record);
        }
        RecordTimeUtil.inst.printfAll();


6.测试结果:

133937720.png

关于本程序,有错误地方请大家指正。