当前位置: 首页 > news >正文

[网页五子棋][匹配模块]实现胜负判定,处理玩家掉线

异常处理

手动注入对象

image.png|399
当前发现此处有一个空指针异常问题,我们怀疑 onlineUserManager 是空的

  • 这个 OnlineUserManager 是我们之前通过 Autowired 注入进来的对象image.png
  • 并且不能给 Room 注释 @Component,如果这么写,这就成了单例,但很明显 Room 不是单例,是多例(有多个实例,有很多房间,每一个对局都是一个房间)

当前 Room 不能设为 Spring 组件,但是我们有需要拿到 OnlineUserManagerRoomManager,我们就需要通过手动处理的方式来获取到实例

在入口类中记录

package org.example.java_gobang;  import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.context.ConfigurableApplicationContext;  @SpringBootApplication  
public class JavaGobangApplication {  public static ConfigurableApplicationContext context;  public static void main(String[] args) {  context = SpringApplication.run(JavaGobangApplication.class, args);  }  }

完善 Room 类

我们在 Room 类中的构造方法里面,通过入口类中记录的 context 来手动获取到 RoomManagerOnlineUserManager

public Room() {  onlineUserManager = JavaGobangApplication.context.getBean(OnlineUserManager.class);  roomManager = JavaGobangApplication.context.getBean(RoomManager.class);  
}

实现胜负判定

实现胜负判定:判定棋面上是否出现物资连珠

  • 一行、一列、一个对角线…

image.png|340
如果棋盘上出现了五子连珠,那就一定是和这个新落子的位置是相关的

  • 进行判定的时候,不需判定整个棋盘
  • 只需要以 rowcol 这个位置为中心,判定周围若干个格子即可image.png|231

先以一行为例来,考虑判定过程
image.png|448

  • 一共有有五种可能性
  • 我们观察最左边第一个点,它的运动范围为:(row, col) 这个位置是玩家这次落子的位置
    • 第一种情况:r = rowc = col - 4
    • 第二种情况:r = rowc = col - 3
    • 第三种情况:r = rowc = col - 2
    • 第四种情况:r = rowc = col - 1
    • 第五种情况:r = rowc = col

image.png|585

  • 一共有有五种可能性
  • 我们观察最左边第一个点,它的运动范围为:(row, col) 这个位置是玩家这次落子的位置
    • 第一种情况:r = row - 4c
    • 第二种情况:r = row - 3c
    • 第三种情况:r = row - 2c
    • 第四种情况:r = row - 1c
    • 第五种情况:r = rowc

右对角线

image.png|586

  • 一共有有五种可能性
  • 我们观察最左边第一个点,它的运动范围为:(row, col) 这个位置是玩家这次落子的位置
    • 第一种情况:r = row - 4c = c + 4
    • 第二种情况:r = row - 3c = c + 3
    • 第三种情况:r = row - 2c = c + 2
    • 第四种情况:r = row - 1c = c + 1
    • 第五种情况:r = rowc

左对角线

image.png|510

  • 一共有有五种可能性
  • 我们观察最左边第一个点,它的运动范围为:(row, col) 这个位置是玩家这次落子的位置
    • 第一种情况:r = rowc = col
    • 第二种情况:r = row + 1c = col + 1
    • 第三种情况:r = row + 2c = col + 2
    • 第四种情况:r = row + 3c = col + 3
    • 第五种情况:r = row + 4c = col + 4

完整代码

  • 如果游戏分出胜负,则返回玩家的 id;如果未分出胜负,则返回 0
  • 棋盘中值为 1,表示是玩家 1 的落子;值为 2,表示是玩家 2 的落子
  • 检查胜负的时候,以当前落子位置为中心,检查所有相关的行、列、对角线即可,不必遍历整个棋盘
private int checkWinner(int row, int col, int chess) {  // 1. 检查所有的行  //    先遍历这五种情况  for (int c = col - 4; c <= col; c++) {  // 针对其中的一种情况,来判定中这五个子是不是连在一起了  // 不管这个五个子得连着,而且还得和玩家落的子是一样的  try {  if (board[row][c] == chess  && board[row][c + 1] == chess  && board[row][c + 2] == chess  && board[row][c + 3] == chess  && board[row][c + 4] == chess) {  // 构成了五子连珠,胜负已分!  return chess == 1 ? user1.getUserId() : getUser2().getUserId();  }  } catch (ArrayIndexOutOfBoundsException e) {  // 如果出现数组下标越界的情况,就在这里直接忽略这个异常  continue;  }  }  // 2. 检查所有的列  //    先遍历五种情况  for (int r = row - 4; r <= row; r++) {  try {  if (board[r][col] == chess  && board[r + 1][col] == chess  && board[r + 2][col] == chess  && board[r + 3][col] == chess  && board[r + 4][col] == chess) {  // 构成了五子连珠,胜负已分!  return chess == 1 ? user1.getUserId() : user2.getUserId();  }  } catch (ArrayIndexOutOfBoundsException e) {  // 如果出现数组下标越界的情况,就在这里直接忽略这个异常  continue;  }  }  // 3. 检查右对角线  for (int r = row - 4, c = col + 4; r <= row && c >= col ; r++, c--) {  try {  if(board[r][c] == chess  && board[r + 1][c - 1] == chess  && board[r + 2][c - 2] == chess  && board[r + 3][c - 3] == chess  && board[r + 4][c - 4] == chess) {  //构成五子连珠,胜负已分!  return chess == 1 ? user1.getUserId() : user2.getUserId();  }  }catch (ArrayIndexOutOfBoundsException e) {  // 如果出现数组下标越界的情况,就在这里直接忽略这个异常  continue;  }  }  // 4. 检查左对角线  for (int r = row = 4, c = col - 4; r <= row && c <= col ; r++, c++) {  try {  if (board[r][c] == chess  && board[r + 1][c + 1] == chess  && board[r + 2][c + 2] == chess  && board[r + 3][c + 3] == chess  && board[r + 4][c + 4] == chess) {  //构成五子连珠,胜负已分!  return chess == 1 ? user1.getUserId() : user2.getUserId();  }  }catch (ArrayIndexOutOfBoundsException e) {  // 如果出现数组下标越界的情况,就在这里直接忽略这个异常  continue;  }  // 胜负未分,就直接返回 0 了  return 0;  }

相关问题

1. 更新玩家分数和场次

当前玩家比赛完成之后,胜负场数和分数都没有发生改变

  • 这里就需要修改数据库

修改 userMapper.java

import org.apache.ibatis.annotations.Mapper;  /**  * 接口里面创建一些典型的方法  */  
@Mapper  
public interface UserMapper {  // 往数据库中插入一个用户,用于注册功能  void insert(User user);  // 根据用户名,来查询用户的详细信息,用于登录功能  User selectByName(String userName);  // 总比赛场数 +1,获胜场数 +1,天梯分数 +30    void userWin(int userId);  // 总比赛场数 +1,获胜场数 不变,天梯分数 -30    void userLose(int userId);  
}
  • 加入两个更新数据的方法

修改 `userMapper.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="org.example.java_gobang.model.UserMapper">  <insert id="insert">  insert into user values(null, #{username}, #{password}, 1000, 0, 0);  </insert>  <select id="selectByName" resultType="org.example.java_gobang.model.User">  select * from user where username = #{username};  </select>  <update id="userWin">  update user set totalCount = totalCount + 1, winCount = winCount + 1, score = score + 30  where userId = #{userId}    </update>  <update id="userLose">  update user set totalCount = totalCount + 1,  scroe = score - 30  where userId = #{userId}    </update>  
</mapper>
  • 增加两个更新操作

最后找到 Room 类中的 putChess 方法,在销毁房间前,进行更新操作 image.png

// 5. 如果胜负已分,就把 room 从房间管理器中销毁  
if (response.getWinner() != 0) {  System.out.println("游戏结束!房间即将销毁! roomId=" + roomId + " 获胜方为:" + response.getWinner());  // 更新获胜方和失败方的信息  int winUserId = response.getWinner();  int loseUserId = response.getWinner() == user1.getUserId() ? user2.getUserId() : user1.getUserId();  userMapper.userWin(winUserId);  userMapper.userLose(loseUserId);  // 销毁房间  roomManager.remove(roomId, user1.getUserId(), user2.getUserId());  
}

2. 玩家掉线

在当前玩家出现掉线的情况,就需要通知对手:你自动获胜

之前的代码中,对与掉线的情况,是进行过检测的 image.png|451

  • 这个不完备,还需要做更及时,更完备的判定 (一方掉线,另一方立即获胜)

我们找到 gameAPI 中的 handleTransportErrorafterConnectionClosed 方法

  • 在这两个方法末尾,都调用一个新的方法 noticeThatUserWin
// 通知对手获胜了  
private void noticeThatUserWin(User user) throws IOException {  // 1. 根据当前玩家,找到玩家所在的房间  Room room = roomManager.getRoomByUserId(user.getUserId());  if (room == null) {  // 这个情况,意味着房间已经被释放了,也就没有“对手”了  System.out.println("当前房间已经释放,无需通知对手!");  return;  }  // 2. 根据房间找到对手  User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();  // 3. 找到对手的在线状态  WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());  if (webSocketSession == null) {  // 这就意味着对手也掉线了  System.out.println("对手也已经掉线了,无需通知! ");  return;  }  // 4. 构造一个响应,来通知对手,你是获胜方  GameResponse resp = new GameResponse();  resp.setMessage("putChess");  resp.setUserId(thatUser.getUserId());  resp.setWinner(thatUser.getUserId());  webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp))); // 5. 更新玩家的分数信息  int winUserId = thatUser.getUserId();  int loseUserId = user.getUserId();  userMapper.userWin(winUserId);  userMapper.userLose(loseUserId);  // 6. 释放房间对象  roomManager.remove(room.getRoomId(), room.getUser1().getUserId(), room.getUser2().getUserId());
}

userAPI 中的 login 部分代码进行修改

@GetMapping("/userInfo")  
@ResponseBody  
public Object getUserInfo(HttpServletRequest req) {  try {  HttpSession httpSession = req.getSession(false);  User user = (User) httpSession.getAttribute("user");  // 拿着这个 user 对象,去数据库中找,找到最新的数据  User newUser = userMapper.selectByName(user.getUsername());  return newUser;  }catch (NullPointerException e) {  return new User();  }  
}

相关文章:

  • 测试面试题 手机号验证码登录测试用例
  • 论文导读 | 动态图存储与事务处理系统总结
  • 敏捷开发中如何避免过度加班
  • 代码随想录 算法训练 Day22:回溯算法part01
  • AIGC 基础篇 高等数学篇 03 中值定理与导数应用
  • 大数据学习(130)-zookeeper
  • Linux系统-基本指令(6)
  • 幂等性:保障系统稳定的关键设计
  • C++内联函数(inline)的作用
  • BUU MISC(持续更新)
  • Linux容器篇、第一章docker命令总结表
  • NLP学习路线图(二十二): 循环神经网络(RNN)
  • 【Python指南】离线安装顽固复杂的第三方库指南
  • 嵌入式系统中常用的开源协议
  • (1-6-3)Java 多线程
  • 深度解析ArrayList
  • Java DLL依赖缺失解决思路和修复过程(Windows版本)
  • django paramiko 跳转登录
  • C++ 使用 ffmpeg 解码本地视频并获取每帧的YUV数据
  • 如何用AI高效运营1000+Tiktok矩阵账号
  • 自己可以学做网站吗/小红书关键词排名优化
  • 梦织做网站/外贸软件排行榜
  • 做网站需要备案吗/口碑营销方案怎么写
  • 沧州黄骅市贴吧/登封搜索引擎优化
  • wordpress博客vieu/seo测试工具
  • 网站推广软件/搜索网站有哪些