Bot代码的执行(微服务)

news2024/10/7 17:25:15

负责接收一段代码,把代码扔到我们的队列当中、每一次我们去运行一段代码
运行结束之后、把我们的结果返回给我们的服务器

先把依赖复制过来、我们需要动态的把用户传过来的Java代码编译然后执行
需要加入依赖joor-java-8、用Java的代码的写法举例子
未来自己实现的时候可以换成任意语言、在这个线程里面是有一堆的bot代码等待执行
然后我们会有一个队列、每次我们从队列中取出一个bot代码来执行
执行的时候用的是一个Java的包会方便的去帮我们动态编译一段Java代码
未来可以在云端把他换成一个docker、你在云端自动启动一个docker容器
给docker设置一个内存上限200M再去docker中动态执行一段Java代码用timeout
去限制某一个程序的执行时间、这样又安全又可以支持其他语言
所以这里我们用Java一个包来动态执行一段Java代码
开一个线程用我们的joor去动态执行一段代码

 

实现Service和Controller

实现完service之后
去实现我们的controller、怎么将SpringBoot和docker关联?
SpringBoot里面可以直接执行shell命令
注意要用127.0.0.1因为我们的config里写的是127.0.0.1

前端加入选择框

每一步的传递都需要加上额外的参数
botid、前端先加上一个框
中间加上一个复选框、需要动态的获得我们的bot列表
这样我们的bot就传过来了、我们需要把bot的信息传回到前端

把前面每一个通信都加上BotId

前面通信的时候每一次通信都是没有加botid的
userid、我们需要把前面每一个通信都加上botid 对应service层也要加上
发的时候收的时候都需要传我们的id

判断人或者机器+编码

在创建完地图执行nextstep的时候
判断一下当前的玩家是人还是代码、如果是代码的话就需要像我们的微服务发送一
段代码、然后让她自动去算、如果是人来操作的话就要等待用户的输入
所以这里需要判断一下如果player的botid=-1的话表示是由人来操作
编码的时候我们随意编只要把我们的编码编成字符串就可以
第一段传的是地图信息、中间用#号隔开

消费者生产者模型

微服务可以不断的去接收用户的一个输入
当接收的代码比较多的时候、我们应该把他们所有接收到的代码放到一个队列里面
我们每接收到一个任务、消费者是一个单独的线程
苦力不断是完成任务,每来一个任务检测队列是否为空如果队列不空,从对头拿出
代码执行、执行完之后再去检查

consume里面开一个新的线程

consume里面开一个新的线程
如果被awit阻塞住后就会被唤醒、然后执行
实现一下consume操作、需要在consume里面开一个新的线程
这样我们就可以从前端动态的传一份代码过来传完之后动态的编译一遍
编译完之后我们就可以动态的调用它编译后的结果

代码执行,此项目只支持java代码的执行,用的是joor java 8实现

可扩展为docker实现,设置内存上限,时间,用命令可执行所有语言代码,并具备一定安全性,因为docker与运行环境隔绝

让BotRunning System获得到前端选择的Bot

新建Bot执行微服务项目 

右键backendcloud->新建->模块

导包

改pom

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>2.7.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
        <dependency>
            <groupId>org.jooq</groupId>
            <artifactId>joor-java-8</artifactId>
            <version>0.9.14</version>
        </dependency>
    </dependencies>
。

BotRunningSystem接收前端选择的botId

文件结构

botrunningsystem
    config
        RestTemplateConfig.java
        SecurityConfig.java
    controller
        BotRunningController.java
    service
        impl
            BotRunningServiceImpl.java
        BotRunningService.java
    BotRunningSystemApplication.java

将BotRunningSystem/Main.java 更名为 BotRunningSystemApplication.java

 

@SpringBootApplication
public class BotRunningSystemApplication {
    public static void main(String[] args) {
        SpringApplication.run(BotRunningSystemApplication.class, args);
    }
}

接口

public interface BotRunningService {

    String addBot(Integer userId, String botCode, String input);
}

接口实现

@Service
public class BotRunningServiceImpl implements BotRunningService {

    @Override
    public String addBot(Integer userId, String botCode, String input) {
        System.out.println("add bot: " + userId + " " + botCode + " " + input);
        return "add bot success";
    }
}

控制器

@RestController
public class BotRunningController {
    @Autowired
    private BotRunningService botRunningService;

    @PostMapping("/bot/add/")
    public String addBot(@RequestParam MultiValueMap<String, String> data) {
        Integer userID = Integer.parseInt(Objects.requireNonNull(data.getFirst("user_id")));
        String botCode = data.getFirst("bot_code");
        String input = data.getFirst("input");
        return botRunningService.addBot(userID, botCode, input);
    }
}

权限控制(网关)

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/bot/add/").hasIpAddress("127.0.0.1")
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated();
    }
}

服务间发送消息的RestTemplate


@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

端口配置

在resources新建文件

Application.properties

server.port=3002

前端选择Bot

匹配界面

添加选择操作方式

MatchGround.vue

<template>
    <div class="matchground">
        <div class="row">
            <div class="col-4">
                ...
            </div>
            <div class="col-4">
                <div class="user-select-bot">
                    <select class="form-select" aria-label="Default select example" v-model="select_bot">
                        <option value="-1" selected>亲自出马</option>
                        <option v-for="bot in bots" :key="bot.id" :value="bot.id">{{ bot.title }}</option>
                    </select>
                </div>
            </div>
            <div class="col-4">
                ...
            </div>
            <div class="col-12" style="text-align: center; padding-top: 15vh;">
                ...
            </div>
        </div>
    </div>
</template>

<script>
import { ref } from 'vue';
import { useStore } from 'vuex';
import $ from 'jquery';

export default {
    setup() {
        ...

        let bots = ref([]);
        let select_bot = ref("-1");

        const click_match_btn = () => {
            if(match_btn_info.value === "开始匹配") {
                match_btn_info.value = "取消";
                store.state.pk.socket.send(JSON.stringify({
                    event: "start-matching",
                    bot_id: select_bot.value,
                }));
            }

            ...
        }; 

        const refresh_bots = () => {
            $.ajax({
                url: "http://127.0.0.1:3000/user/bot/getlist/",
                type: "get",
                headers: {
                    'Authorization': "Bearer " + store.state.user.token,
                },
                success(resp) {
                    bots.value = resp;
                }
            })
        };
        refresh_bots();
        return {
            ...

            bots,
            select_bot,
        }
    }
}
</script>

<style scoped>
...

div.user-select-bot {
    padding-top: 20vh;
}
div.user-select-bot > select {
    width: 60%;
    margin: 0 auto;
}
</style>

后端接收bot

BackEnd接收Bot
WebSocketServer接收到匹配请求,将bot传给匹配服务

@Component
// url链接:ws://127.0.0.1:3000/websocket/**
@ServerEndpoint("/websocket/{token}")  // 注意不要以'/'结尾
public class WebSocketServer {
    ...

    private void startMatching(Integer botId) {
        System.out.println("start matching!");
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("user_id", this.user.getId().toString());
        data.add("rating", this.user.getRating().toString());
        data.add("bot_id", botId.toString());
        restTemplate.postForObject(addPlayerUrl, data, String.class);
    }

    ...

    @OnMessage
    public void onMessage(String message, Session session) {
        ...

        if("start-matching".equals(event)) {
            startMatching(data.getInteger("bot_id"));
        }

        ...
    }

    ...
}

Matching System接收Bot

Matching System接收到backend传的botId,将bot传给BotRunningSystem服务

控制器


@RestController
public class MatchingController {
    @Autowired
    private MatchingService matchingService;

    // 参数不能使用普通map,MultiValueMap和普通map的区别时,这个是一个键对应多个值
    @PostMapping("/player/add/")
    public String addPlayer(@RequestParam MultiValueMap<String, String> data) {
        ...

        Integer botId = Integer.parseInt(Objects.requireNonNull(data.getFirst("bot_id")));
        return matchingService.addPlayer(userId, rating, botId);
    }

    ...
}

接口


public interface MatchingService {

    String addPlayer(Integer userId, Integer rating, Integer botId);

    ...
}

接口实现


@Service
public class MatchingServiceImpl implements MatchingService {
    public static final MatchingPool matchingPool = new MatchingPool();

    @Override
    public String addPlayer(Integer userId, Integer rating, Integer botId) {
        System.out.println("Add Player: " + userId + " " + rating + " " + botId);
        matchingPool.addPlayer(userId, rating, botId);
        return "add player success";
    }

    ...
}

匹配池


// 匹配池是多线程的
@Component
public class MatchingPool extends Thread {
    ...

    public void addPlayer(Integer userId, Integer rating, Integer botId) {
        // 在多个线程(匹配线程遍历players时,主线程调用方法时)会操作players变量,因此加锁
        lock.lock();
        try {
            players.add(new Player(userId, rating, botId, 0));
        } finally {
            lock.unlock();
        }
    }

    ...
}

匹配池的Player


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Player {
    private Integer userId;
    private Integer rating;
    private Integer botId;
    private Integer waitingTime;
}

匹配池返回结果加上botId

// 匹配池是多线程的
@Component
public class MatchingPool extends Thread {
    ...

    private void sendResult(Player a, Player b) {   // 返回匹配结果
        System.out.println("send result: " + a + " " + b);
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("a_id", a.getUserId().toString());
        data.add("a_bot_id", a.getBotId().toString());
        data.add("b_id", b.getUserId().toString());
        data.add("b_bot_id", b.getBotId().toString());
        restTemplate.postForObject(startGameUrl, data, String.class);
    }

    ...
}

BackEnd接收匹配成功返回的botId

StartGameController.java


@RestController
public class StartGameController {
    ...

    @PostMapping("/pk/start/game/")
    public String startGame(@RequestParam MultiValueMap<String, String> data) {
        Integer aId = Integer.parseInt(Objects.requireNonNull(data.getFirst("a_id")));
        Integer aBotId = Integer.parseInt(Objects.requireNonNull(data.getFirst("a_bot_id")));
        Integer bId = Integer.parseInt(Objects.requireNonNull(data.getFirst("b_id")));
        Integer bBotId = Integer.parseInt(Objects.requireNonNull(data.getFirst("b_bot_id")));
        return startGameService.startGame(aId, aBotId, bId, bBotId);
    }
}

StartGameService.java

public interface StartGameService {

    String startGame(Integer aId, Integer aBotId, Integer bId, Integer bBotId);
}

StartGameServiceImpl.java

@Service
public class StartGameServiceImpl implements StartGameService {

    @Override
    public String startGame(Integer aId, Integer aBotId, Integer bId, Integer bBotId) {
        System.out.println("start gameL: " + aId + " " + bId);
        WebSocketServer.startGame(aId, aBotId, bId, bBotId);
        return "start game success";
    }
}

WebSocketServer.java


@Component
// url链接:ws://127.0.0.1:3000/websocket/**
@ServerEndpoint("/websocket/{token}")  // 注意不要以'/'结尾
public class WebSocketServer {
    ...

    public static BotMapper botMapper;

    ...

    @Autowired
    public void setBotMapper(BotMapper botMapper) {
        WebSocketServer.botMapper = botMapper;
    }

    ...

    public static void startGame(Integer aId, Integer aBotId, Integer bId, Integer bBotId) {
        User a = userMapper.selectById(aId), b = userMapper.selectById(bId);
        Bot botA = botMapper.selectById(aBotId), botB = botMapper.selectById(bBotId);

        Game game = new Game(
                13,
                14,
                20,
                a.getId(),
                botA,
                b.getId(),
                botB);

        ...
    }

    ...

}

Player.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Player {
    private Integer id;
    private Integer botId;  // -1表示亲自出马,否则表示用AI打
    private String botCode;

    ...
}

WebSocketServer.java

将RestTemplate变成public,若是代码输入则屏蔽人的输入

@Component
// url链接:ws://127.0.0.1:3000/websocket/**
@ServerEndpoint("/websocket/{token}")  // 注意不要以'/'结尾
public class WebSocketServer {
    ...

    public static RestTemplate restTemplate;

    private void move(int direction) {
        if(game.getPlayerA().getId().equals(user.getId())) {
            if(game.getPlayerA().getBotId().equals(-1))     // 亲自出马则接收输入
                game.setNextStepA(direction);
        } else if(game.getPlayerB().getId().equals(user.getId())) {
            if(game.getPlayerB().getBotId().equals(-1))
                game.setNextStepB(direction);
        }
    }

    ...
}

Game.java

public class Game extends Thread {
    ...

    private static final String addBotUrl = "http://127.0.0.1:3002/bot/add/";


    public Game(
            Integer rows,
            Integer cols,
            Integer inner_walls_count,
            Integer idA,
            Bot botA,
            Integer idB,
            Bot botB
    ) {
        this.rows = rows;
        this.cols = cols;
        this.inner_walls_count = inner_walls_count;
        this.g = new int[rows][cols];

        Integer botIdA = -1, botIdB = -1;
        String botCodeA = "", botCodeB = "";

        if(botA != null) {
            botIdA = botA.getId();
            botCodeA = botA.getContent();
        }
        if(botB != null) {
            botIdB= botB.getId();
            botCodeB = botB.getContent();
        }

        playerA = new Player(idA, botIdA, botCodeA, rows - 2, 1, new ArrayList<>());
        playerB = new Player(idB, botIdB, botCodeB, 1, cols - 2, new ArrayList<>());
    }


    private String getInput(Player player) {    // 将当前局面信息编码成字符串
        // 地图#my.sx#my.sy#my操作#you.sx#you.sy#you操作
        Player me, you;
        if(playerA.getId().equals(player.getId())) {
            me = playerA;
            you = playerB;
        } else {
            me = playerB;
            you = playerA;
        }

        return getMapString() + "#" +
                me.getSx() + "#" +
                me.getSy() + "#(" +
                me.getStepsString() + ")#" +    // 加()是为了预防操作序列为空
                you.getSx() + "#" +
                you.getSy() + "#(" +
                you.getStepsString() + ")";
    }

    private void sendBotCode(Player player) {
        if(player.getBotId().equals(-1)) return;
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("user_id", player.getId().toString());
        data.add("bot_code", player.getBotCode());
        data.add("input", getInput(player));
        WebSocketServer.restTemplate.postForObject(addBotUrl, data, String.class);
    }

    // 接收玩家的下一步操作
    private boolean nextStep() {
        // 每秒五步操作,因此第一步操作是在200ms后判断是否接收到输入。
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        sendBotCode(playerA);
        sendBotCode(playerB);

        ...
    }

    ...

}

BotRunning System的实现

 

思路:

评测器是一个经典的生产者消费者模型,此服务生产者会将任务放进一个队列中,
消费者是单独的一个线程,当有任务就会从队头立即执行;并且关键问题是评测
器的执行代码不能单纯的用sleep 1s去判断是否有任务,这样很影响评测体验,
因此需要用到Condition Variable,当有任务执行,无任务等待。

文件结构

 matchingsystem
    service
        impl
            utils
                Bot.java
                BotPool.java
                Consumer.java
    utils
        Bot.java
        BotInterface.java

Bot的实现

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Bot {
    Integer userId;
    String botCode;
    String input;
}

 

BotPoll的实现

虽然队列没用消息队列,但是因为我们写了条件变量与锁的操作,所以等价于消息队列

public class BotPool extends Thread {
    // 以下的锁和条件变量加不加static都可以,因为BotPool只有一个
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private final Queue<Bot> bots = new LinkedList<>();

    public void addBot(Integer userId, String botCode, String input) {
        lock.lock();
        try {
            bots.add(new Bot(userId, botCode, input));
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    private void consume(Bot bot) {

    }

    @Override
    public void run() {
        while(true) {
            lock.lock();
            if(bots.isEmpty()) {
                try {
                    // 若执行了await会自动释放锁
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    lock.unlock();
                    break;
                }
            } else {
                Bot bot = bots.remove();
                lock.unlock();
                // 耗时操作,因此要在释放锁之后执行
                consume(bot);
            }
        }
    }

BotRunningServiceImpl.java

加任务

@Service
public class BotRunningServiceImpl implements BotRunningService {
    public static final BotPool botPool = new BotPool();

    @Override
    public String addBot(Integer userId, String botCode, String input) {
        System.out.println("add bot: " + userId + " " + botCode + " " + input);
        botPool.addBot(userId, botCode, input);
        return "add bot success";
    }
}

 BotPool线程的启动

@SpringBootApplication
public class BotRunningSystemApplication {
    public static void main(String[] args) {
        BotRunningServiceImpl.botPool.start();
        SpringApplication.run(BotRunningSystemApplication.class, args);
    }
}

BotInterface.java

用户写Bot实现的接口

BotInterface.java


public interface BotInterface {
    Integer nextMove(String input);
}

Bot.java

public class Bot implements BotInterface {

    @Override
    public Integer nextMove(String input) {
        return 0;   // 向上走
    }
}

Consumer的实现

docker与沙箱分别有什么区别

public class Consumer extends Thread {
    private Bot bot;

    public void startTimeout(long timeout, Bot  bot) {
        this.bot = bot;
        this.start();

        // 在 程序运行结束后 或 程序在指定timeout时间后还未执行完毕 直接中断代码执行
        try {
            this.join(timeout);
            this.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            this.interrupt();
        }
    }

    private String addUid(String code, String uid) {    // 在code中的Bot类名后添加uid
        int k = code.indexOf(" implements BotInterface");
        return code.substring(0, k) + uid + code.substring(k);
    }

    @Override
    public void run() {
        UUID uuid = UUID.randomUUID();
        String uid = uuid.toString().substring(0, 8);
        BotInterface botInterface = Reflect.compile(
                "com.kob.botrunningsystem.utils.Bot" + uid,
                addUid(bot.getBotCode(), uid)
        ).create().get();

        Integer direction = botInterface.nextMove(bot.getInput());
        System.out.println("move-direction: " + bot.getUserId() + " " + direction);
    }
}

调用Consumer

public class BotPool extends Thread {
    ...

    private void consume(Bot bot) {
        Consumer consumer = new Consumer();
        consumer.startTimeout(2000, bot);
    }

    ...
}

前端Bug的修改

在游戏结束后,点到其他页面再点回pk页面,结果没有消失

PkIndexView.vue

<template>
    ...
</template>

<script>
...

export default {
    ...

    setup() {
        const store = useStore();
        const socketUrl = `ws://127.0.0.1:3000/websocket/${store.state.user.token}/`;

        store.commit("updateLoser", 'none');

        ...
    }
}
</script>

<style scoped>

</style>

将Bot执行结果传给前端

BackEnd接收Bot代码的结果

文件结构

backend
    controller
        pk
            ReceiveBotMoveController.java
    service
        impl
            pk
                ReceiveBotMoveServiceImpl.java
        pk
            ReceiveBotMoveService.java
 

接口

public interface ReceiveBotMoveService {

    String receiveBotMove(Integer userId, Integer direction);
}

 WebSocketServer操作类

将game改为public

...

@Component
// url链接:ws://127.0.0.1:3000/websocket/**
@ServerEndpoint("/websocket/{token}")  // 注意不要以'/'结尾
public class WebSocketServer {
    ...

    public Game game = null;

    ...
}

接口实现

@Service
public class ReceiveBotMoveServiceImpl implements ReceiveBotMoveService {

    @Override
    public String receiveBotMove(Integer userId, Integer direction) {
        System.out.println("receive bot move: " + userId + " " + direction + " ");
        if(WebSocketServer.users.get(userId) != null) {
            Game game = WebSocketServer.users.get(userId).game;
            if(game != null) {
                if(game.getPlayerA().getId().equals(userId)) {
                    game.setNextStepA(direction);
                } else if(game.getPlayerB().getId().equals(userId)) {
                    game.setNextStepB(direction);
                }
            }
        }
        return "receive bot move success";
    }
}

控制器

@RestController
public class ReceiveBotMoveController {
    @Autowired
    private ReceiveBotMoveService receiveBotMoveService;

    @PostMapping("/pk/receive/bot/move/")
    public String receiveBotMove(@RequestParam MultiValueMap<String, String> data) {
        Integer userId = Integer.parseInt(Objects.requireNonNull(data.getFirst("user_id")));
        Integer direction = Integer.parseInt(Objects.requireNonNull(data.getFirst("direction")));
        return receiveBotMoveService.receiveBotMove(userId, direction);
    }
}

权限控制(网关)

放行接口


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 放行这两个接口
                .antMatchers("/user/account/token/", "/user/account/register/", "/getKaptcha").permitAll()
                .antMatchers("/pk/start/game/", "/pk/receive/bot/move/").hasIpAddress("127.0.0.1")
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    ...
}

BotRunningSystem返回Bot执行结果

Consumer.java

package com.kill9.botsystem.service.impl.utils;

import com.kill9.botsystem.utils.BotInterface;
import org.joor.Reflect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.UUID;

@Component
public class Consumer extends Thread{

    private Bot bot;

    private static RestTemplate restTemplate;
    private final static String receiveBotMoveUrl = "http://127.0.0.1:3000/pk/receive/bot/move";

    @Autowired
    public void setRestTemplate(RestTemplate restTemplate){
        Consumer.restTemplate = restTemplate;
    }
    public void startTimeOut(long timeout,Bot bot) {
        this.bot = bot;
        //开一个新的线程去执行run
        this.start();

        //当前线程继续执行join ,等待时间超过timeout
        try{
            this.join(timeout);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            this.interrupt();//中断当前线程
        }
    }

    private String addUid(String code,String uid){
        //在code中的Bot类名 后添加Uid
        int k = code.indexOf(" implements com.kill9.botsystem.utils.BotInterface");
        return code.substring(0,k)+uid+code.substring(k);
    }
    @Override
    public void run() {
        //动态编译一个代码  如果类重名的话,只会编译一次 ,为了能够让类名不一样 + uuid
        UUID uuid = UUID.randomUUID();
        //前8位
        String uid = uuid.toString().substring(0,8);
        BotInterface botInterface = Reflect.compile(
                "com.kill9.botsystem.utils.Bot"+uid,
                addUid(bot.getBotCode(), uid)
        ).create().get();
        Integer direction = botInterface.nextMove(bot.getInput());
        System.out.println("move-d:" + bot.getUserId() + " "+ direction);
        MultiValueMap<String ,String > data = new LinkedMultiValueMap<>();
        data.add("user_id",bot.getUserId().toString());
        data.add("direction",direction.toString());
        //结果返回给backend服务器
        restTemplate.postForObject(receiveBotMoveUrl,data,String.class);
    }
}

 

厉害的bot代码:

public class Bot implements BotInterface {

    static class Cell {
        public int x, y;
        public Cell(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    // 检查当前回合,蛇的长度是否会增加
    private boolean check_tail_increasing(int step) {
        if(step <= 10) return true;
        return step % 3 == 1;
    }

    public List<Cell> getCells(int sx, int sy, String steps) {
        steps = steps.substring(1, steps.length() - 1);
        List<Cell> res = new ArrayList<>();

        int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
        int x = sx, y = sy;
        int step = 0;
        res.add(new Cell(x, y));
        for(int i = 0; i < steps.length(); i++) {
            int d = steps.charAt(i) - '0';
            x += dx[d];
            y += dy[d];
            res.add(new Cell(x, y));
            if(!check_tail_increasing(++step)) {
                res.remove(0);
            }
        }
        return res;
    }

    @Override
    public Integer nextMove(String input) {
        // 地图#my.sx#my.sy#(my操作)#you.sx#you.sy#(you操作)
        String[] strs = input.split("#");
        int[][] g = new int[13][14];
        for(int i = 0, k = 0; i < 13; i++) {
            for(int j = 0; j < 14; j++, k++) {
                if(strs[0].charAt(k) == '1') {
                    g[i][j] = 1;
                }
            }
        }

        int aSx = Integer.parseInt(strs[1]), aSy = Integer.parseInt(strs[2]);
        int bSx = Integer.parseInt(strs[4]), bSy = Integer.parseInt(strs[5]);

        List<Cell> aCells = getCells(aSx, aSy, strs[3]);
        List<Cell> bCells = getCells(bSx, bSy, strs[6]);

        for(Cell c : aCells) g[c.x][c.y] = 1;
        for(Cell c : bCells) g[c.x][c.y] = 1;

        int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
        for(int i = 0; i < 4; i++) {
            int x = aCells.get(aCells.size() - 1).x + dx[i];
            int y = aCells.get(aCells.size() - 1).y + dy[i];
            if(x >= 0 && x < 13 && y >= 0 && y < 14 && g[x][y] == 0) {
                return i;
            }
        }
        return 0;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/25167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云无影研发负责人任晋奎:端云技术创新,打造全新用户体验

11 月 4 日&#xff0c;阿里云无影研发负责人任晋奎在 2022 云栖大会“无影技术创新与应用实践”论坛上&#xff0c;发表了题为《端云技术创新&#xff0c;打造全新用户体验》的演讲&#xff0c;对无影端云开放架构的技术能力做了深入浅出的分享&#xff0c;解读了无影如何以技…

实现制作动漫版的你---动漫风格迁移网络AnimeGANv2

AI依然火 最近居家太无聊了&#xff0c;无意见逛到了AI社区&#xff0c;发现最近AI算法热度还是很高&#xff0c;什么AI绘画&#xff0c;还有什么AI作诗&#xff0c;写歌。可以说&#xff0c;人工智能领域在一步步挑战人类的高度&#xff0c;从循规蹈矩的简单工作&#xff0c;…

【笔试强训】Day1

&#x1f308;欢迎来到笔试强训专栏 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&#x…

阿里云云数据库Redis的核心概念以及正确购买姿势(十五)

文章目录1.云数据库Redis基本介绍1.1.什么是云数据库Redis1.2.Redis标准版单副本架构1.3.Redis标准版双副本架构1.4.Redis集群版单副本架构1.5.Redis集群双副本架构1.6.Redis集群的直连架构1.7.Redis集群读写分离架构2.购买云数据库Redis2.1.创建Redis实例2.2.配置商品类型及可…

WPF 控件专题 TreeView控件详解

1、TreeView介绍 命名空间&#xff1a;System.Windows.Controls TreeView 表示一个控件&#xff0c;该控件在树结构&#xff08;其中的项可以展开和折叠&#xff09;中显示分层数据。 TreeView 是一个 ItemsControl&#xff0c;这意味着它可以包含任何类型的对象的集合 (&a…

进程的通信 - WM_COPYDATA

在Windows程序中&#xff0c;各个进程之间常常需要交换数据&#xff0c;进行数据通讯。其中使用SendMessage向另一进程发送WM_COPYDATA消息是一种比较经济实惠的方法。 WM_COPYDATA通信需要将传递的消息封装在COPYDATASTRUCT结构体中&#xff0c;在SendMessage消息传递函数时作…

[附源码]java毕业设计校园超市进销存管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于PETALINUX的以太网调试

1、建立工程 2、编译工程&#xff0c;导出xsa文件 3、建立petalinux工程 4、生成boot和image.ub文件&#xff0c;将文件复制到系统为fat32的sd卡中。 5、插上串口&#xff0c;插上网线 6、vm中需要桥接网络&#xff0c;有正确的IP地址 7、通过串口&#xff0c;配置板卡的以太…

前端面试题(JS部分)

目录一&#xff0c; 数据类型1&#xff0c;什么是引用类型&#xff0c;值类型&#xff1f;2&#xff0c;哪些值类型3&#xff0c;哪些引用类型4&#xff0c;判断数据类型5&#xff0c;typeof判断6&#xff0c;instanceof7&#xff0c;construtor二&#xff0c;浅拷贝 / 深拷贝1…

好物推荐:文字转语音朗读软件哪个好?

不知道大家有没有像我一样的&#xff0c;对“语音播放”、“语音朗读”是又爱又恨的。有时工作繁忙&#xff0c;而同事或客户发来语音资料需要整理时&#xff0c;就会很烦躁&#xff0c;因为语音的整理起来比文字的繁琐一些。而像有时在家休息&#xff0c;还得看一些资料时&…

亚马逊鲲鹏系统之亚马逊买家号养号时需要注意哪些?

经常做亚马逊测评的朋友应该都清楚&#xff0c;亚马逊买家号养号是很有必要的&#xff0c;如果一个新买家号没有养过号就进行刷单测评&#xff0c;那么是很容易就死号了的。但是想要养好号也不是那么容易的&#xff0c;需要注意好多问题。 第一&#xff1a;养号的重点是号&…

Linux 软连接与硬链接

文件链接概念介绍&#xff1a; 软连接文件&#xff1a; 概念介绍&#xff1a;用于存放指向源文件的路径信息&#xff08;文件快捷方式&#xff09; 应用场景&#xff1a;对于要操作文件数据提供方便 硬链接介绍 硬链接不能跨分区创建 补充&#xff1a; 1.目录硬链接数代…

k8s-List机制及resourceVersion语义

kube-apiserver 核心职责 提供Restful API&#xff1b;代理集群组件&#xff0c;如dashboard、流式日志、kubectl exec 会话&#xff1b;缓存全量的etcd 数据且无状态服务可水平扩展。 kube-List操作 绝大部分情况下&#xff0c;kube-apiserver 都会直接从本地缓存提供服务&…

MybatisX插件 逆向工程

MybatisX插件 逆向工程 目录MybatisX插件 逆向工程安装插件使用插件安装插件 File->Settings->Plugins 使用插件 点击DataSource->点击“” 点击DataSource->点击MySQL 驱动选择MySQL for 5.1 输入主机IP、端口号、数据库的用户名和密码->点击Test Connec…

浅谈系统架构设计-从架构设计原理、架构设计原则、架构设计方法展开

我们工作中一直强调要做架构设计、系分&#xff0c;最近前端同学在追求前端质量提升的时候&#xff0c;也在进行架构设计、前端系分的推广&#xff0c;那到底什么是架构设计和系分&#xff1f;该怎么做架构设计和系分&#xff1f;本文尝试对架构设计进行全面的介绍和分享&#…

【机器学习】SVM入门-硬间隔模型

本站原创文章&#xff0c;转载请说明来自《老饼讲解-机器学习》ml.bbbdata.com 目录 一. SVM的目标和思想 1.1 SVM硬间隔模型的原始目的 1.2 SVM的直接目标 1.3 什么是支持向量 二. SVM的支持平面的表示方式 2.1 支持面表示方式的初步思路 2.2 初步思路的缺陷与改进 2…

基于Vue+ElementUI+MySQL+Express的学生管理系统(2)

2.搭建Express Web服务器 1.在E:\vue\shiyan9路径下创建score-server的文件夹放置Express Web服务器的相关文件。 图3 初始化package.json的信息 2.用Vscode打开score-server。并创建如图4所示的项目结构图。 以下目录中的文件依次为&#xff1a; 考试信息增删改查的接口。…

【BP回归预测】改进的鲸鱼算法优化BP神经网络回归预测(多输入单输出)【含Matlab源码 2184期】

⛄一、鲸鱼算法优化BP神经网络简介 1 鲸鱼优化算法&#xff08;Whale Optimization Algorithm&#xff0c;WOA&#xff09;简介 鲸鱼优化算法(WOA)&#xff0c;该算法模拟了座头鲸的社会行为&#xff0c;并引入了气泡网狩猎策略。 1.1 灵感 鲸鱼被认为是世界上最大的哺乳动物…

DBCO-mPEG,5 kDa,二苯并环辛炔-MPEG, 5 KDA白色结晶状

●外观以及性质&#xff1a; DBCO-mPEG,5 kDa白色结晶状&#xff0c;其的分子量有多种&#xff0c;10kDa&#xff0c;20kDa&#xff0c;30kDa等一系列的。DBCO活化的聚乙二醇化试剂通过无铜“点击化学”反应与叠氮化物反应&#xff0c;形成稳定的三唑&#xff0c;不需要铜催化剂…

evpp mingw64编译过程

evpp是奇虎360的基于libevent的网络通讯库&#xff0c;源代码不错&#xff1a; https://github.com/Qihoo360/evpp/blob/master/readme_cn.md 它提供vs的编译结构&#xff0c;我用的是mingw64cmake&#xff0c;使用gnu12&#xff0c;把修改过程分享给大家。 步骤1. 修改CMake…