项目实战--网页五子棋(游戏房间)(6)

上期我们基本完成了匹配功能,这期我们来实现游戏房间相关的代码

由于同时会存在多个游戏房间,我们实现游戏房间时可以再实现一个房间管理器,用于管理游戏房间,我们约定房间管理器通过哈希表来管理游戏房间。

1. 游戏房间实体类

java 复制代码
public class Room {
    private String roomId;
    private User user1;
    private User user2;

    public Room() {
        roomId = UUID.randomUUID().toString();
    }
    public String getRoomId() {
        return roomId;
    }

    public void setRoomId(String roomId) {
        this.roomId = roomId;
    }

    public User getUser1() {
        return user1;
    }

    public void setUser1(User user1) {
        this.user1 = user1;
    }

    public User getUser2() {
        return user2;
    }

    public void setUser2(User user2) {
        this.user2 = user2;
    }
}

这里我们使用UUID来作为房间ID,方便存进哈希表时作为键

2. 房间管理器实体类

java 复制代码
package org.ting.j20250110_gobang.game;

import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

@Component
public class RoomManager {
    private ConcurrentHashMap<String, Room> roomIdToRoom = new ConcurrentHashMap<>();
    private ConcurrentHashMap<Integer, String> userIdToRoomId = new ConcurrentHashMap<>();
    public void add(String roomId, Room room, Integer userId1, Integer userId2) {
        roomIdToRoom.put(roomId, room);
        userIdToRoomId.put(userId1, roomId);
        userIdToRoomId.put(userId2, roomId);
    }
    public Room getRoomByRoomId(String roomId) {
        return roomIdToRoom.get(roomId);
    }
    public Room getRoomByUserId(Integer userId) {
        return roomIdToRoom.get(userIdToRoomId.get(userId));
    }
}

这里我们创建了两个哈希表,一个维护房间id到游戏房间的映射,一个维护用户id到游戏房间的映射,此时我们就可以通过add方法把两个用户加入一个游戏房间内

java 复制代码
public void handlerMatch(Queue<User> matchQueue) {
        try {
            //对操作的队列加锁保证线程安全
            synchronized (matchQueue) {
                //1.检测队列中是否有两个元素
                while(matchQueue.size() < 2) {
                    matchQueue.wait();
                }

                //2.从队列中取出两个玩家
                User user1 = matchQueue.poll();
                User user2 = matchQueue.poll();

                //3.获取到两个玩家的会话信息
                WebSocketSession session1 = onlineUserManager.getFromHall(user1.getUserId());
                WebSocketSession session2 = onlineUserManager.getFromHall(user2.getUserId());

                System.out.println("匹配成功," + user1.getUsername() + "," + user2.getUsername() + "正在进入游戏房间");

                //4.把两个玩家放到一个游戏房间中
                Room room = new Room();
                roomManager.add(room.getRoomId(), room, user1.getUserId(), user2.getUserId());

                //5.给用户返回匹配成功的响应
                MatchResponse response = new MatchResponse();
                response.setOk(true);
                response.setMessage("success");
                String json = objectMapper.writeValueAsString(response);
                session1.sendMessage(new TextMessage(json));
                session2.sendMessage(new TextMessage(json));
            }
        }catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }

3. 游戏房间前端代码

room.html:

html 复制代码
<!DOCTYPE html>
<html lang="chch">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>游戏房间</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/room.css">
</head>
<body>
    <div class="nav">五子棋</div>
    <div class="container">
        <div>
            <!-- 棋盘区域 -->
            <canvas id="chess" width="450px" height="450px">

            </canvas>
            <div id="screen">等待玩家连接</div>
        </div>
    </div>
    <script src="js/script.js"></script>
    <!-- <script src="js/jquery.min.js"></script> -->
</body>
</html>

script.js:

javascript 复制代码
gameInfo = {
    roomId: null,
    thisUserId: null,
    thatUserId: null,
    isWhite: true,
}

//
// 设定界面显示相关操作
//

function setScreenText(me) {
    let screen = document.querySelector('#screen');
    if (me) {
        screen.innerHTML = "轮到你落子了!";
    } else {
        screen.innerHTML = "轮到对方落子了!";
    }
}

//
// 初始化 websocket
//
// TODO

//
// 初始化一局游戏
//
function initGame() {
    // 是我下还是对方下. 根据服务器分配的先后手情况决定
    let me = gameInfo.isWhite;
    // 游戏是否结束
    let over = false;
    let chessBoard = [];
    //初始化chessBord数组(表示棋盘的数组)
    for (let i = 0; i < 15; i++) {
        chessBoard[i] = [];
        for (let j = 0; j < 15; j++) {
            chessBoard[i][j] = 0;
        }
    }
    let chess = document.querySelector('#chess');
    let context = chess.getContext('2d');
    context.strokeStyle = "#BFBFBF";
    // 背景图片
    let logo = new Image();
    logo.src = "image/sky.jpeg";
    logo.onload = function () {
        context.drawImage(logo, 0, 0, 450, 450);
        initChessBoard();
    }

    // 绘制棋盘网格
    function initChessBoard() {
        for (let i = 0; i < 15; i++) {
            context.moveTo(15 + i * 30, 15);
            context.lineTo(15 + i * 30, 430);
            context.stroke();
            context.moveTo(15, 15 + i * 30);
            context.lineTo(435, 15 + i * 30);
            context.stroke();
        }
    }

    // 绘制一个棋子, me 为 true
    function oneStep(i, j, isWhite) {
        context.beginPath();
        context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
        context.closePath();
        var gradient = context.createRadialGradient(15 + i * 30 + 2, 15 + j * 30 - 2, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0);
        if (!isWhite) {
            gradient.addColorStop(0, "#0A0A0A");
            gradient.addColorStop(1, "#636766");
        } else {
            gradient.addColorStop(0, "#D1D1D1");
            gradient.addColorStop(1, "#F9F9F9");
        }
        context.fillStyle = gradient;
        context.fill();
    }

    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 发送坐标给服务器, 服务器要返回结果

            oneStep(col, row, gameInfo.isWhite);
            chessBoard[row][col] = 1;
        }
    }
}

initGame();
相关推荐
Lizhihao_3 分钟前
JAVA-堆 和 堆排序
java·开发语言
极客先躯8 分钟前
高级java每日一道面试题-2025年3月21日-微服务篇[Nacos篇]-什么是Nacos?
java·开发语言·微服务
工业互联网专业16 分钟前
基于springboot+vue的动漫交流与推荐平台
java·vue.js·spring boot·毕业设计·源码·课程设计·动漫交流与推荐平台
雷渊19 分钟前
深入分析Spring的事务隔离级别及实现原理
java·后端·面试
rebel30 分钟前
Java获取excel附件并解析解决方案
java·后端
并不会42 分钟前
多线程案例-单例模式
java·学习·单例模式·单线程·多线程·重要知识
数据攻城小狮子43 分钟前
Java Spring Boot 与前端结合打造图书管理系统:技术剖析与实现
java·前端·spring boot·后端·maven·intellij-idea
m0_5557629044 分钟前
struct 中在c++ 和c中用法区别
java·c语言·c++
HongXuan-Yuan1 小时前
系统设计:高并发策略与缓存设计
java·分布式·高并发
Alt.91 小时前
MyBatis基础五(动态SQL,缓存)
java·sql·mybatis