上期我们完成了websocket建立连接后的数据初始化,今天我们完成落子交互的具体代码:
这里我们先复习一下,之前约定好的落子请求与响应包含的字段:
1. 发送落子请求
我们在script.js文件中找到落子的相关方法,增加发送请求的代码:
chess.onclick = function (e) {
if (over) {
return;
}
if (!me) {
return;
}
let x = e.offsetX;
let y = e.offsetY;
// 注意, 横坐标是列, 纵坐标是行
let col = Math.floor(x / 30);
let row = Math.floor(y / 30);
if (chessBoard[row][col] == 0) {
// TODO 发送坐标给服务器, 服务器要返回结果
webSocket.send(JSON.stringify({
message: 'putChess',
userId: gameInfo.userId1,
row: row,
col: col
}));
oneStep(col, row, gameInfo.isBlack);
chessBoard[row][col] = 1;
}
}
2. 处理请求发送响应
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//1. 获取用户信息
User user = (User)session.getAttributes().get("user");
//2. 获取请求信息
GameRequest request = objectMapper.readValue(message.getPayload(),GameRequest.class);
//3. 调用游戏房间中的putChess方法实现落子
Room room = roomManager.getRoomByUserId(user.getUserId());
room.putChess(request);
}
实现putChess:
private static int ROW = 15;
private static int COL = 15;
//棋盘, 0表示未落子,1表示玩家1的子,2表示玩家2的子
private int[][] board = new int[ROW][COL];
//处理落子请求
public void putChess(GameRequest request) throws IOException {
//1. 判断落子玩家
int chess = request.getUserId() == user1.getUserId() ? 1 : 2;
int row = request.getRow();
int col = request.getCol();
//2. 落子
if(board[row][col] != 0) {
System.out.println("[" + row + "," + col + "]已经有子了");
return;
}
board[row][col] = chess;
//3. 判断是否获胜
int winnerId = checkWinner(row, col) ? request.getUserId() : -1;
//4. 给房间中的玩家返回响应
GameResponse response = new GameResponse();
response.setMessage("putChess");
response.setUserId(request.getUserId());
response.setRow(row);
response.setCol(col);
response.setWinnerId(winnerId);
//通过OnlineUserManager获取房间中的玩家
WebSocketSession session1 = onlineUserManager.getFromRoom(user1.getUserId());
WebSocketSession session2 = onlineUserManager.getFromRoom(user2.getUserId());
//判断是否有玩家下线
if(session1 == null) {
//玩家1下线,玩家2获胜
response.setWinnerId(user2.getUserId());
System.out.println("玩家1掉线");
}
if(session1 == null) {
//玩家2下线,玩家1获胜
response.setWinnerId(user1.getUserId());
System.out.println("玩家2掉线");
}
String resp = objectMapper.writeValueAsString(response);
if(session1 != null) {
session1.sendMessage(new TextMessage(resp));
}
if(session2 != null) {
session2.sendMessage(new TextMessage(resp));
}
if(response.getWinnerId() != -1) {
System.out.println("分出胜负, 游戏房间:" + roomId + "即将销毁");
roomManager.remove(roomId, user1.getUserId(), user2.getUserId());
}
}
注意:我们并没有把Room注册为Spring组件,进行依赖注入时,需要在构造方法中手动注入:
public Room() {
roomId = UUID.randomUUID().toString();
onlineUserManager = J20250110GoBangApplication.context.getBean(OnlineUserManager.class);
objectMapper = J20250110GoBangApplication.context.getBean(ObjectMapper.class);
roomManager = J20250110GoBangApplication.context.getBean(RoomManager.class);
}
实现checkWinner方法检测是否获胜:
private boolean checkWinner(int row, int col) {
int count = 1;
//判断行是否五子连珠
for(int i = col + 1; i < COL; i++) {
if(board[row][i] == board[row][col]) {
count++;
}else{
break;
}
}
for(int i = col - 1; i >= 0; i--) {
if(board[row][i] == board[row][col]) {
count++;
}else{
break;
}
}
if(count >= 5) {
return true;
}
//判断列是否五子连珠
count = 1;
for(int i = row + 1; i < ROW; i++) {
if(board[i][col] == board[row][col]) {
count++;
}else{
break;
}
}
for(int i = row - 1; i >= 0; i--) {
if(board[i][col] == board[row][col]) {
count++;
}else{
break;
}
}
if(count >= 5) {
return true;
}
//判断左上到右下斜线是否五子连珠
count = 1;
for(int i = row + 1, j = col + 1; i < ROW && j < COL; i++, j++) {
if(board[i][j] == board[row][col]) {
count++;
}else{
break;
}
}
for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if(board[i][j] == board[row][col]) {
count++;
}else{
break;
}
}
if(count >= 5) {
return true;
}
//判断右上到左下斜线是否五子连珠
count = 1;
for(int i = row - 1, j = col + 1; i >= 0 && j < COL; i--, j++) {
if(board[i][j] == board[row][col]) {
count++;
}else{
break;
}
}
for(int i = row + 1, j = col - 1; i < ROW && j >= 0; i++, j--) {
if(board[i][j] == board[row][col]) {
count++;
}else{
break;
}
}
if(count >= 5) {
return true;
}
return false;
}
3. 处理落子响应
在之前初始化游戏的代码中,我们在游戏初始化完成时会调用initGame()方法:
于是我们可以在initGame方法末尾中修改onmessages使之变为处理落子响应的方法:
webSocket.onmessage = function (e) {
let resp = JSON.parse(e.data);
console.log(resp);
if (resp.message != 'putChess') {
console.log("响应类型错误");
return;
}
//判断是谁落子
if (resp.userId == gameInfo.userId1) {
//根据对应棋子颜色绘制棋子
oneStep(resp.col, resp.row, gameInfo.isBlack);
} else if (resp.userId == gameInfo.userId2) {
//根据对应棋子颜色绘制棋子
oneStep(resp.col, resp.row, !gameInfo.isBlack);
} else {
console.log("响应出错 userId:" + resp.userId);
return;
}
// 给对应的位置设置为1,表示有子
chessBoard[resp.row][resp.col] = 1;
//交互落子方
me = !me;
setScreenText(me);
//判断游戏是否结束
if (resp.winnerId == gameInfo.userId1) {
over = true;
alert("你赢了!!!");
location.href = "/hall.html";
}
if (resp.winnerId == gameInfo.userId2) {
over = true;
alert("你输了");
location.href = "/hall.html";
}
}