文章目录

  • 文章链接
  • RunManFunc——地图事件逻辑类
  • RunStart——继承Runnable实现多线程
  • Utils——工具类
  • MapUtils——地图工具类
  • ThreadPoolUtils——线程池工具类
  • application.yml——配置文件

RunManFunc——地图事件逻辑类

package com.sport.map;

import com.sport.entity.RunMan;
import com.sport.map.condition.model.Condition;
import com.sport.map.condition.model.Effect;
import com.sport.map.condition.model.ImmunizationCondition;
import com.sport.map.event.MapEvent;
import com.sport.utils.MapUtils;

/**
 * <p>
 * 处理Map Event的各种方法。
 *
 * <pre>
 * 触发条件: 速度 名次 时间 掷骰子 > >= == < <= number
 * 免疫条件: 速度 名次 时间 掷骰子 > >= == < <= number'
 * 效应: 速度 负重 距离 '+ - = number'
 * 效应参数: timeOfDuration 持续时间单位为秒, -1 时为永久
 * </pre>
 *
 * @author jiajun.b.zhang
 * @version V1.0.0
 */
public class RunMapFunc {
    /** 记录比赛文言 */
    private String gameCommentary;
    /** 判断时间是否被触发 */
    private boolean executeFlag = true;

    /**
     * <p>
     * 处理事件,先进行事件的类型判断,然后进行事件的条件判断。 如果不满足触发条件,则不对事件进行处理。如果满足触发条件,则进行
     * 免疫条件判断,如果不满足免疫条件,则将事件影响添加到事件寄存器。 如果满足免疫条件,则进行事件免疫。
     *
     * @param mapEvent 地图事件
     * @param runMan   运动员
     * @return 返回事件文言
     */
    public String startEvents(MapEvent mapEvent, RunMan runMan) {
        gameCommentary = "";
        if (mapEvent.getEffectsOfEvent().getConditions().size() > 0) {
            //确定地图事件是否拥有触发条件
            mapEvent.getEffectsOfEvent().getConditions().forEach((conditionItem) -> {
                //循环触发条件
                if (checkCondition(conditionItem, runMan, mapEvent.getRunManRank(runMan)) && executeFlag) {
                    //检查地图事件是否被触发
                    //记录触发事件后文言
                    gameCommentary = conditionItem.getDescription() + mapEvent.getDescription();
                    //判断事件是否被免疫
                    judgmentInfluence(mapEvent, runMan);
                    //将执行标志改为false
                    executeFlag = false;
                }
            });
        } else {
            //记录事件文言
            gameCommentary = mapEvent.getDescription();
            //判断事件是否被免疫
            judgmentInfluence(mapEvent, runMan);
        }
        //将执行标志改为true
        executeFlag = true;
        return gameCommentary;
    }

    /**
     * <p>判断地图事件是否被免疫,如果未被免疫,首先判断影响是否为永久,如果为永久对运动员的基础属性值进行修改。然后将事件影响记录到状态寄存器
     * @param mapEvent 地图事件
     * @param runMan 运动员
     */
    public void judgmentInfluence(MapEvent mapEvent, RunMan runMan) {
        if (!checkImmunizationCondition(mapEvent.getEffectsOfEvent().getImmunizationCondition(), runMan,
                mapEvent.getRunManRank(runMan))) {
            //判断事件是否被免疫
            mapEvent.getEffectsOfEvent().getEffects().forEach((effectItem) -> {
                if (effectItem.getOptions().getTimeOfDuration() == -1) {
                    //判断事件影响是否为永久
                    //事件影响
                    checkEffectPermanent(effectItem, runMan);
                }
                //添加事件影响到状态寄存器
                runMan.addStateRegister(effectItem);
                gameCommentary = gameCommentary + effectItem.getDescription();
            });
        } else {
            gameCommentary = gameCommentary + mapEvent.getEffectsOfEvent().getImmunizationCondition().getDescription();
        }
    }

    /**
     * <p>根据触发条件判断事件是否被触发
     * @param condition 地图事件触发条件
     * @param runMan    运动员
     * @param rank      运动员排名
     * @return 事件是否被触发
     *
     */
    public boolean checkCondition(Condition condition, RunMan runMan, double rank) {
        //选择检查项目名
        switch (condition.getItem()) {
        case "速度":
            return checkSymbol(runMan.getSpeed(), condition.getValue(), condition.getOperator());
        case "名次":
            return checkSymbol(rank, condition.getValue(), condition.getOperator());
        case "时间":
            return checkSymbol(runMan.getMileage() / runMan.getSpeed() * 1000, condition.getValue(),
                    condition.getOperator());
        case "掷骰子":
            return checkSymbol(MapUtils.getRandom(), condition.getValue(), condition.getOperator());
        default:
            return false;
        }
    }

    /**
     * <p>根据免疫条件判断事件是否被触发
     * @param immunizationCondition 地图事件免疫条件
     * @param runMan                运动员
     * @param rank                  运动员排名
     * @return 事件是否被免疫
     */
    public boolean checkImmunizationCondition(ImmunizationCondition immunizationCondition, RunMan runMan, double rank) {
        //选择检查项目名
        switch (immunizationCondition.getItem()) {
        case "速度":
            return checkSymbol(runMan.getSpeed(), immunizationCondition.getValue(),
                    immunizationCondition.getOperator());
        case "名次":
            return checkSymbol(rank, immunizationCondition.getValue(), immunizationCondition.getOperator());
        case "时间":
            return checkSymbol(runMan.getMileage() / runMan.getSpeed() * 1000, immunizationCondition.getValue(),
                    immunizationCondition.getOperator());
        case "掷骰子":
            return checkSymbol(MapUtils.getRandom(), immunizationCondition.getValue(),
                    immunizationCondition.getOperator());
        default:
            return false;
        }
    }

    /**
     * <p>添加事件影响
     * @param effect 地图事件影响
     * @param runMan 运动员
     */
    public void checkEffectTime(Effect effect, RunMan runMan) {
        //选择检查项目名
        switch (effect.getItem()) {
        case "速度":
            runMan.setSpeedState(addState(runMan.getSpeed(), effect.getValue(), effect.getOperator()));
            break;
        case "负重":
            runMan.setAddedWeightState(addState(runMan.getAddedWeight(), effect.getValue(), effect.getOperator()));
            break;
        case "距离":
            runMan.setMileage(addState(runMan.getMileage(), effect.getValue(), effect.getOperator()));
            break;
        default:
            break;
        }
    }

    /**
     * <p>添加事件影响
     * @param effect 地图事件影响
     * @param runMan 运动员
     */
    public void checkEffectPermanent(Effect effect, RunMan runMan) {
        switch (effect.getItem()) {
        case "速度":
            runMan.setSpeed(addState(runMan.getSpeed(), effect.getValue(), effect.getOperator()));
            break;
        case "负重":
            runMan.setAddedWeight(addState(runMan.getAddedWeight(), effect.getValue(), effect.getOperator()));
            break;
        case "距离":
            runMan.setMileage(addState(runMan.getMileage(), effect.getValue(), effect.getOperator()));
            break;
        default:
            break;
        }
    }

    /**
     * <p> 判断检查符号
     * @param runManValue    运动员属性值
     * @param conditionValue 地图事件触发条件所需值
     * @param symbol         判断符号
     * @return 是否触发地图事件
     */
    public boolean checkSymbol(double runManValue, double conditionValue, String symbol) {
        switch (symbol) {
        case ">":
            return runManValue > conditionValue;
        case ">=":
            return runManValue >= conditionValue;
        case "==":
            return runManValue == conditionValue;
        case "<":
            return runManValue < conditionValue;
        case "<=":
            return runManValue <= conditionValue;
        default:
            return false;
        }
    }

    /**
     * <p> 添加事件影响
     * @param runManValue 运动员属性值
     * @param stateValue  状态值
     * @param symbol      运算符号
     * @return 状态运算值
     */
    public double addState(double runManValue, double stateValue, String symbol) {
        switch (symbol) {
        case "+":
            return runManValue + stateValue;
        case "-":
            return runManValue - stateValue;
        case "=":
            return stateValue;
        default:
            return 0;
        }
    }
}

RunStart——继承Runnable实现多线程

package com.sport.services;

import com.sport.entity.GameResult;
import com.sport.entity.RunMan;
import com.sport.map.RunMap;
import com.sport.map.RunMapFunc;
import com.sport.mapper.PlayerInfoMapper;
import com.sport.utils.MapUtils;

/**
 * <p> 比赛处理
 *
 * @author jiajun.b.zhang
 * @version V1.0.0
 *
 */
public class RunStart implements Runnable {
    /** 比赛地图 */
    private RunMap runMap;
    /** 运动员 */
    private RunMan runMan;
    /** 执行标志 */
    private boolean startFlag = true;
    /** 每次执行所跑时间 */
    private int second;
    /** 执行总时长 */
    private int gameDuration;
    /** 执行文言 */
    private String gameCommentary;
    /** 本次比赛Id */
    private String gameId;
    /** 执行储存比赛结果操作所需的PlayerInfoMapper */
    private PlayerInfoMapper playerInfoService;
    /** 处理事件的逻辑方法 */
    private RunMapFunc runMapFunc;
    /** msg信息 */
    private String msgString;

    public RunStart(RunMap runMap, RunMan runMan, PlayerInfoMapper playerInfoService,
            String gameId) {
        this.runMap = runMap;
        this.runMan = runMan;
        this.second = 10;
        this.gameDuration = 0;
        this.playerInfoService = playerInfoService;
        this.runMapFunc = new RunMapFunc();
        this.gameId = gameId;
        this.gameCommentary="";
    }

    @Override
    public void run() {
        while (this.startFlag) {
            beginPlaying();
        }
    }

    /**
     * 开始比赛
     */
    private void beginPlaying() {
        MapUtils.prepare(this.second);
        for (int i = 0; i < this.second; i++) {
            //初始化运动员状态
            runMan.initState();
            //当前距离
            runMan.setMileage(runMan.getMileage() + (runMan.getSpeedState() / 3.6));
            //遍历事件合集
            runMap.getEvents().forEach((eventItem) -> {
                if (runMan.getMileage() >= eventItem.getOccurredAt()) {
                    //进入事件
                    if (!eventItem.getRunManRank().contains(runMan)) {
                        //查看是否已触发事件
                        //加入排名
                        eventItem.addRunManRank(runMan);
                        //添加message
                        msgString = runMapFunc.startEvents(eventItem, runMan);
                        if (msgString != null) {
                            //写入文言
                            gameCommentary += msgString;
                        }
                    }
                }
            });
            gameDuration++;
            if (runMan.getMileage() >= runMap.getDistance()) {
                // 执行标志
                this.startFlag = false;
                // 加入最终排名
                runMap.addRunManRank(runMan);
                // 将比赛结果写入数据库
                playerInfoService.addGameResult(new GameResult(runMan.getId(), gameId, MapUtils.getDate(),
                        runMap.getName(),
                        runMap.getRunManRank(runMan), (int) (runMan.getMileage() / gameDuration * 3.6), gameDuration,
                        gameCommentary.replace("null", "")));
               
                break;
            }
            runMan.regressionState();
        }
    }
}

Utils——工具类

MapUtils——地图工具类

package com.sport.utils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.yaml.snakeyaml.Yaml;
import com.sport.entity.RunMan;
import com.sport.map.RunMap;

/**
 * 地图工具包
 * @author jiajun.b.zhang
 * @version V1.0.0
 *
 */
public class MapUtils {
    private static Logger log = LoggerFactory.getLogger(MsgUtils.class);
    private static Yaml yamlTool = new Yaml();

    /**
     * 初始化地图
     * @param url 地图名称
     * @return 比赛地图
     */
    public static RunMap initRunMap(String url) {
        ClassPathResource classPathResource = new ClassPathResource(url + ".yaml");
        try {
            log.info("Init Map");
            return yamlTool.loadAs(classPathResource.getInputStream(), RunMap.class);
        } catch (IOException e) {
            log.error("Failed to import map", e);
            return null;
        } finally {
            log.info("Init Map End");
        }
    }

    /**
     * 模拟摇骰子
     * @return 随机数
     */
    public static int getRandom() {
        return (int) (Math.random() * 6) + 1;
    }

    /**
     * 等待事件
     * @param second 秒
     */
    public static void prepare(int second) {
        try {
            Thread.sleep(10 * second);
        } catch (InterruptedException e) {
            log.error("Failed Prepare", e);
        }
    }

    /**
     * 获取当前日期
     * @return 日期
     */
    public static Date getDate() {
        return new Date();
    }
    
    /**
     * 获取运动员排名
     * @return 运动员排名
     */
    public static int getRank(RunMan runMan,ArrayList<RunMan> runManRank) {
    	try {
    		for (int i = 0; i < runManRank.size(); i++) {
        		if (runManRank.get(i).getId()==runMan.getId()) {
        			return i+1;
    			}
    		}
    		throw new NullPointerException("The player is currently unranked, RunManId: "+runMan.getId());
		} catch (NullPointerException e) {
			log.error("MapEvent Error:"+e.getMessage(),e);
        	return 0;
		}
    }
}

ThreadPoolUtils——线程池工具类

package com.sport.utils;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * ThreadPoolUtils
 * @author jiajun.b.zhang
 * @version V1.0.0
 *
 */
@Component
public class ThreadPoolUtils {

    /** corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去; */
    private static int corePoolSize;
    /** maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量; */
    private static int maximumPoolSize;
    /** keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁; */
    private static int keepAliveTime;
    /** unit:keepAliveTime的单位 */
    private static TimeUnit unit = TimeUnit.MILLISECONDS;
    /** workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种; */
    private static BlockingQueue<Runnable> workQueue = new SynchronousQueue<Runnable>();

    /**
     * 读取配置文件中的corePoolSize到静态变量
     * @param corePoolSize 线程数量
     */
    @Value("${ThreadPool.corePoolSize}")
    public void setCorePoolSize(int corePoolSize) {
        ThreadPoolUtils.corePoolSize = corePoolSize;
    }

    /**
     * 读取配置文件中的maximumPoolSize到静态变量
     * @param maximumPoolSize 最大线程数量
     */
    @Value("${ThreadPool.maximumPoolSize}")
    public void setMaximumPoolSize(int maximumPoolSize) {
        ThreadPoolUtils.maximumPoolSize = maximumPoolSize;
    }

    /**
     * 读取配置文件中的keepAliveTime到静态变量
     * @param keepAliveTime 线程池中空闲线程等待工作的超时时间
     */
    @Value("${ThreadPool.keepAliveTime}")
    public void setKeepAliveTime(int keepAliveTime) {
        ThreadPoolUtils.keepAliveTime = keepAliveTime;
    }

    /**
     * 创建线程池
     * @return 线程池
     */
    public static ThreadPoolExecutor buildRunGameThreadPoolExecutor() {
        return new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue);

    }
}

application.yml——配置文件

server:
  port: ####端口号
 
spring:
  datasource:
    username: ####数据库用户名
    password: ####数据库密码
    url: jdbc:mysql://localhost:3306/#数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.jdbc.Driver
 
mybatis:
  mapper-locations: classpath*:mapping/*Mapper.xml
  type-aliases-package: com.sport.entity

ThreadPool: 
  corePoolSize: 20
  maximumPoolSize: 20
  keepAliveTime: 0