From 28c2c2dc04bc9f7ae4e49d123e00236ae38bd2f3 Mon Sep 17 00:00:00 2001 From: dyh <2825183872@qq.com> Date: Wed, 24 Aug 2022 04:37:12 +0800 Subject: [PATCH] =?UTF-8?q?version-2.2[=E5=89=8D=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=93=8D=E4=BD=9C+=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B+=E8=AF=BB=E5=86=99=E9=94=81+=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93:=E5=BD=95=E5=83=8F]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kob/backend/consumer/WebSocketServer.java | 40 ++- .../com/kob/backend/consumer/utils/Cell.java | 12 + .../com/kob/backend/consumer/utils/Game.java | 232 +++++++++++++++++- .../kob/backend/consumer/utils/Player.java | 58 +++++ .../com/kob/backend/mapper/RecordMapper.java | 9 + .../java/com/kob/backend/pojo/Record.java | 30 +++ web/src/assets/scripts/GameMap.js | 34 +-- web/src/assets/scripts/Snake.js | 2 +- web/src/components/GameMap.vue | 6 +- web/src/components/ResultBoard.vue | 71 ++++++ web/src/store/pk.js | 24 +- web/src/views/pk/PkIndexView.vue | 35 ++- 12 files changed, 517 insertions(+), 36 deletions(-) create mode 100644 backend/src/main/java/com/kob/backend/consumer/utils/Cell.java create mode 100644 backend/src/main/java/com/kob/backend/consumer/utils/Player.java create mode 100644 backend/src/main/java/com/kob/backend/mapper/RecordMapper.java create mode 100644 backend/src/main/java/com/kob/backend/pojo/Record.java create mode 100644 web/src/components/ResultBoard.vue diff --git a/backend/src/main/java/com/kob/backend/consumer/WebSocketServer.java b/backend/src/main/java/com/kob/backend/consumer/WebSocketServer.java index cf72ef8..47c5d01 100644 --- a/backend/src/main/java/com/kob/backend/consumer/WebSocketServer.java +++ b/backend/src/main/java/com/kob/backend/consumer/WebSocketServer.java @@ -4,6 +4,7 @@ package com.kob.backend.consumer; import com.alibaba.fastjson.JSONObject; import com.kob.backend.consumer.utils.Game; import com.kob.backend.consumer.utils.JwtAuthentication; +import com.kob.backend.mapper.RecordMapper; import com.kob.backend.mapper.UserMapper; import com.kob.backend.pojo.User; import org.springframework.beans.factory.annotation.Autowired; @@ -22,7 +23,7 @@ public class WebSocketServer { //ConcurrentHashMap 的优势在于兼顾性能和线程安全,一个线程进行写操作时, //它会锁住一小部分,其他部分的读写不受影响,其他线程访问没上锁的地方不会被阻塞。 //(userId,WebSocketServer实例:连接) - final private static ConcurrentHashMap users = new ConcurrentHashMap<>(); + public final static ConcurrentHashMap users = new ConcurrentHashMap<>(); //匹配池 final private static CopyOnWriteArraySet matchpool = new CopyOnWriteArraySet<>(); //当前用户 @@ -33,11 +34,13 @@ public class WebSocketServer { private Game game ; //加入数据库 private static UserMapper userMapper; + public static RecordMapper recordMapper; @Autowired public void setUserMapper(UserMapper userMapper){ WebSocketServer.userMapper = userMapper; } - + @Autowired + public void setRecordMapper(RecordMapper recordMapper) { WebSocketServer.recordMapper = recordMapper;} @OnOpen @@ -47,7 +50,7 @@ public class WebSocketServer { this.session = session ;//将session存起来--》一个用户一个session System.out.println("connected!"); Integer userId = JwtAuthentication.getUserId(token);//获取id - this.user = userMapper.selectById(userId);//获取用户 + this.user = userMapper.selectById(userId);//获取当前用户 if(this.user != null){//存在用户 加入连接 users.put(userId,this);//用户链接--》加入到集合中 @@ -77,21 +80,36 @@ public class WebSocketServer { matchpool.remove(a); matchpool.remove(b); - Game game =new Game(13,14,20); + //一局游戏的线程 + Game game =new Game(13,14,20,a.getId(),b.getId()); game.createMap(); + //a,b共同的地图==>将地图赋给a,b对应的连接 + users.get(a.getId()).game = game ; + users.get(b.getId()).game = game ;//b连接的地图 + game.start(); + + JSONObject respGame = new JSONObject(); + respGame.put("a_id",game.getPlayerA().getId()); + respGame.put("a_sx",game.getPlayerA().getSx()); + respGame.put("a_sy",game.getPlayerA().getSy()); + respGame.put("b_id",game.getPlayerB().getId()); + respGame.put("b_sx",game.getPlayerB().getSx()); + respGame.put("b_sy",game.getPlayerB().getSy()); + respGame.put("map",game.getG()); + JSONObject respA = new JSONObject();//返回给a respA.put("event", "start-matching"); respA.put("opponent_username", b.getUsername()); respA.put("opponent_photo", b.getPhoto()); - respA.put("gamemap", game.getG()); + respA.put("game", respGame); users.get(a.getId()).sendMessage(respA.toJSONString());//获取a对应的连接,向前端传递信息(String) JSONObject respB = new JSONObject();//返回给b respB.put("event", "start-matching"); respB.put("opponent_username", a.getUsername()); respB.put("opponent_photo", a.getPhoto()); - respB.put("gamemap", game.getG()); + respB.put("game", respGame); users.get(b.getId()).sendMessage(respB.toJSONString());//获取b对应的连接,向前端传递信息(String) } } @@ -101,6 +119,14 @@ public class WebSocketServer { matchpool.remove(this.user); } + public void move(int dirction){ + if(game.getPlayerA().getId().equals(user.getId())){//当前链接是A用户 + game.setNextStepA(dirction); + } else if(game.getPlayerB().getId().equals(user.getId())){//当前链接是B用户 + game.setNextStepB(dirction); + } + } + @OnMessage public void onMessage(String message, Session session) { // 接收前端信息 @@ -111,6 +137,8 @@ public class WebSocketServer { startMatching(); } else if("stop-matching".equals(event)){ stopMatching(); + } else if("move".equals(event)){ + move(data.getInteger("direction")); } } diff --git a/backend/src/main/java/com/kob/backend/consumer/utils/Cell.java b/backend/src/main/java/com/kob/backend/consumer/utils/Cell.java new file mode 100644 index 0000000..0bb56e4 --- /dev/null +++ b/backend/src/main/java/com/kob/backend/consumer/utils/Cell.java @@ -0,0 +1,12 @@ +package com.kob.backend.consumer.utils; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Cell { + int x,y; +} diff --git a/backend/src/main/java/com/kob/backend/consumer/utils/Game.java b/backend/src/main/java/com/kob/backend/consumer/utils/Game.java index eff0bd9..6e200fb 100644 --- a/backend/src/main/java/com/kob/backend/consumer/utils/Game.java +++ b/backend/src/main/java/com/kob/backend/consumer/utils/Game.java @@ -1,24 +1,52 @@ package com.kob.backend.consumer.utils; +import com.alibaba.fastjson.JSONObject; +import com.kob.backend.consumer.WebSocketServer; +import com.kob.backend.pojo.Record; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import java.util.Random; +import java.util.concurrent.locks.ReentrantLock; + +public class Game extends Thread { + + private final Integer rows; + private final Integer cols; + private final Integer inner_walls_count; + private final int[][] g; + private final static int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1}; + private final Player playerA,playerB; + //用户a,b的下一步方向 + private Integer nextStepA = null; + private Integer nextStepB = null; + + private ReentrantLock lock = new ReentrantLock(); -public class Game { + private String status = "playing";//playing-->finished + private String loser = "" ;//all平局,A:A输,B:B输 - final private Integer rows; - final private Integer cols; - final private Integer inner_walls_count; - final private int[][] g; - final private static int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1}; - //构造map规模 - public Game(Integer rows, Integer cols, Integer inner_walls_count) { + public Game(Integer rows, Integer cols, Integer inner_walls_count,Integer idA, Integer idB) { this.rows = rows; this.cols = cols; this.inner_walls_count = inner_walls_count; this.g = new int[rows][cols]; + playerA = new Player(idA,this.rows - 2, 1, new ArrayList<>()); + playerB = new Player(idB,1, this.cols - 2, new ArrayList<>()); + } + + public Player getPlayerA(){ + return this.playerA; + } + + public Player getPlayerB(){ + return this.playerB; } - //货物gamemap + //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + //获取gamemap public int[][] getG() { return g; @@ -95,4 +123,190 @@ public class Game { if (draw()) break; } } + + //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + public void setNextStepA(Integer nextStepA){ + lock.lock(); + try{ + this.nextStepA = nextStepA ; + } finally { + lock.unlock(); + } + } + + public void setNextStepB(Integer nextStepB){ + lock.lock(); + try{ + this.nextStepB = nextStepB ; + } finally { + lock.unlock(); + } + } + + + + private boolean nextStep(){//两名玩家的下一步 + try { + Thread.sleep(200);//因为前端走一格200ms + } catch (InterruptedException e) { + e.printStackTrace(); + } + + //超时5s判断 + for(int i = 1; i <= 50;i ++) + { + try { + Thread.sleep(100); + lock.lock(); + try{ + if(this.nextStepA != null && this.nextStepB != null) { + //记录方向 + playerA.getSteps().add(nextStepA); + playerB.getSteps().add(nextStepB); + return true; + } + } finally { + lock.unlock(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + return false ; + } + //检查这一步是否合法 + //判断cellsA是否合法的,判断cellsB的合法直接调用处反着写即可 + private boolean check_valid(List cellsA,List cellsB) + { + int n = cellsA.size(); + Cell cell = cellsA.get(n-1);//取出最后一步 + + if(g[cell.x][cell.y] == 1 ) return false ; + + + for(int i = 0; i < n-1 ;i++ ){ + if(cell.x == cellsA.get(i).x && cell.y == cellsA.get(i).y){//撞自己 + return false ; + } + } + + for(int i = 0; i < n-1; i++) { + if (cell.x == cellsB.get(i).x && cell.y == cellsB.get(i).y) {//撞另一条蛇 + return false; + } + } + + return true; + } + + //判断loser + private void judge(){//判断两名玩家下一步是否合法 + //取出两条蛇 + List cellsA = playerA.getCells(); + List cellsB = playerB.getCells(); + boolean validA = check_valid(cellsA,cellsB); + boolean validB = check_valid(cellsB,cellsA); + if(!validA || !validB)//结束游戏 + { + this.status = "finished" ; + if(!validA&&!validB){ + this.loser = "all" ; + } else if(!validA){ + this.loser = "A" ; + } else if(!validB){ + this.loser = "B" ; + } + } + + } + + private void sendResult(){//向两个client端公布结果 + JSONObject resp = new JSONObject(); + resp.put("event","result"); + resp.put("loser",loser); + saveToDatabase(); + sendAllMessage(resp.toJSONString()); + } + + private void sendMove() {//向两个client传递移动信息 + lock.lock(); + try{ + JSONObject resp = new JSONObject(); + resp.put("event","move"); + resp.put("a_direction",nextStepA); + resp.put("b_direction",nextStepB); + sendAllMessage(resp.toJSONString()); + this.nextStepA = this.nextStepB = null;//清空下一步 + } finally { + lock.unlock(); + } + } + + public void sendAllMessage(String message){ + //向全端发信息 + WebSocketServer.users.get(playerA.getId()).sendMessage(message); + WebSocketServer.users.get(playerB.getId()).sendMessage(message); + } + + private void saveToDatabase() + { + Record record = new Record( + null, + playerA.getId(), + playerA.getSx(), + playerA.getSy(), + playerB.getId(), + playerB.getSx(), + playerB.getSy(), + playerA.getStepsString(), + playerB.getStepsString(), + getMapString(), + loser, + new Date() + ); + WebSocketServer.recordMapper.insert(record); + } + private String getMapString(){ + StringBuilder res = new StringBuilder(); + for(int i=0;i steps;//存方向0123 + + //蛇是否增长 + private boolean check_tail_increasing(int step){ + if(step <= 10)return true; + if(step % 3 == 1)return true; + return false; + } + + //获取蛇的路径 + public List getCells(){ + List 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 d:steps){ + x = x + dx[d]; + y = y + dy[d]; + res.add(new Cell(x,y)); + if(!check_tail_increasing( ++step)){//不增长,去除尾巴 + res.remove(0); + } + } + return res ; + } + + public String getStepsString() { + StringBuilder res = new StringBuilder(); + for(int x:steps){ + res.append(x); + } + return res.toString(); + } + + + +} diff --git a/backend/src/main/java/com/kob/backend/mapper/RecordMapper.java b/backend/src/main/java/com/kob/backend/mapper/RecordMapper.java new file mode 100644 index 0000000..7de2d89 --- /dev/null +++ b/backend/src/main/java/com/kob/backend/mapper/RecordMapper.java @@ -0,0 +1,9 @@ +package com.kob.backend.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.kob.backend.pojo.Record; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface RecordMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/kob/backend/pojo/Record.java b/backend/src/main/java/com/kob/backend/pojo/Record.java new file mode 100644 index 0000000..b88b864 --- /dev/null +++ b/backend/src/main/java/com/kob/backend/pojo/Record.java @@ -0,0 +1,30 @@ +package com.kob.backend.pojo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Record { + @TableId(type = IdType.AUTO) + private Integer id ; + private Integer aId ; + private Integer aSx ; + private Integer aSy ; + private Integer bId ; + private Integer bSx ; + private Integer bSy ; + private String aSteps ; + private String bSteps ; + private String map ; + private String loser ; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai") + private Date createtime ; +} diff --git a/web/src/assets/scripts/GameMap.js b/web/src/assets/scripts/GameMap.js index ed604fb..a5c9052 100644 --- a/web/src/assets/scripts/GameMap.js +++ b/web/src/assets/scripts/GameMap.js @@ -23,8 +23,8 @@ export class GameMap extends AcGameObject this.inner_walls_count = 20; this.snakes = [ - new Snake({id:0,color:"#4876ED",r:this.rows-2,c:1},this), - new Snake({id:1,color:"#F94848",r:1,c:this.cols-2},this) + new Snake({id:0,color:"#4876ED",r:this.rows-2,c:1},this),//左下 + new Snake({id:1,color:"#F94848",r:1,c:this.cols-2},this) //右上 ] @@ -33,7 +33,6 @@ export class GameMap extends AcGameObject //创建墙 create_wall(){ const g = this.store.state.pk.gamemap ; - console.log(g) for(let r=0;r { - if(e.key === "w") snake0.set_direction(0); - else if(e.key === "d") snake0.set_direction(1); - else if(e.key === 's') snake0.set_direction(2); - else if(e.key === 'a') snake0.set_direction(3); - else if(e.key === 'ArrowUp' ) snake1.set_direction(0); - else if(e.key === 'ArrowRight') snake1.set_direction(1); - else if(e.key === 'ArrowDown') snake1.set_direction(2); - else if(e.key === 'ArrowLeft') snake1.set_direction(3); - // console.log("keydown") + let d = -1; + if(e.key === "w") d = 0; + else if(e.key === "d") d = 1; + else if(e.key === 's') d = 2; + else if(e.key === 'a') d = 3; + + if(d >= 0){ + this.store.state.pk.socket.send(JSON.stringify({ + event:"move", + direction:d, + })) + } }) } - + // else if(e.key === 'ArrowUp' ) snake1.set_direction(0); + // else if(e.key === 'ArrowRight') snake1.set_direction(1); + // else if(e.key === 'ArrowDown') snake1.set_direction(2); + // else if(e.key === 'ArrowLeft') snake1.set_direction(3); + // console.log("keydown") update(){ if(this.check_ready())this.next_step();//2个蛇准备好了下一步-->走下一步 diff --git a/web/src/assets/scripts/Snake.js b/web/src/assets/scripts/Snake.js index a91dc2d..af1f7b4 100644 --- a/web/src/assets/scripts/Snake.js +++ b/web/src/assets/scripts/Snake.js @@ -76,7 +76,7 @@ export class Snake extends AcGameObject{ this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1])); } - if (!this.gamemap.check_valid(this.next_cell)) this.status = "die"; //死亡 + // if (!this.gamemap.check_valid(this.next_cell)) this.status = "die"; //死亡 } //蛇是否增长 diff --git a/web/src/components/GameMap.vue b/web/src/components/GameMap.vue index ee94ee6..179a212 100644 --- a/web/src/components/GameMap.vue +++ b/web/src/components/GameMap.vue @@ -19,7 +19,11 @@ export default { let canvas = ref(null); onMounted(()=>{ - new GameMap(canvas.value.getContext('2d'),parent.value,store); + store.commit( + "updateGameObject", + new GameMap(canvas.value.getContext('2d'),parent.value,store) + ); + // console.log(parent.value);//DOM // console.log(canvas.value);//DOM }) diff --git a/web/src/components/ResultBoard.vue b/web/src/components/ResultBoard.vue new file mode 100644 index 0000000..c31731b --- /dev/null +++ b/web/src/components/ResultBoard.vue @@ -0,0 +1,71 @@ + + + + + \ No newline at end of file diff --git a/web/src/store/pk.js b/web/src/store/pk.js index 9f8c524..597448e 100644 --- a/web/src/store/pk.js +++ b/web/src/store/pk.js @@ -7,6 +7,14 @@ export default { opponent_username:"", opponent_photo:"", gamemap:null, + a_id:0, + a_sx:0, + a_sy:0, + b_id:0, + b_sx:0, + b_sy:0, + gameObject:null, + loser:"none",//none all A B }, getters: { }, @@ -21,8 +29,20 @@ export default { updateStatus(state,status){ state.status = status; }, - updateGamemap(state,gamemap){ - state.gamemap = gamemap ; + updateGame(state,game){ + state.a_id = game.a_id ; + state.a_sx = game.a_sx ; + state.a_sy = game.a_sy ; + state.b_id = game.b_id ; + state.b_sx = game.b_sx ; + state.b_sy = game.b_sy ; + state.gamemap = game.map ; + }, + updateGameObject(state,gameObject){ + state.gameObject = gameObject ; + }, + updateLoser(state,loser){ + state.loser = loser ; } }, actions: { diff --git a/web/src/views/pk/PkIndexView.vue b/web/src/views/pk/PkIndexView.vue index 5afc88d..084234f 100644 --- a/web/src/views/pk/PkIndexView.vue +++ b/web/src/views/pk/PkIndexView.vue @@ -1,20 +1,26 @@