项目实战--网页五子棋(对战功能)(9)

上期我们完成了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";
        }
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

elementplus的cascader级联选择器在懒加载且多选时的一些问题分析

1. 背景 在之前做的一个项目中使用到了element的级联选择器&#xff0c;并且是需要懒加载、多选、父子不关联等等&#xff0c;在选的时候当然没问题&#xff0c;但是回显的时候就会回显不出来&#xff0c;相信大部分伙伴都遇到过这个问题。我在以前出过一篇文章写过关于级联选…

基于PySide6的CATIA零件自动化着色工具开发实践

引言 在汽车及航空制造领域&#xff0c;CATIA作为核心的CAD设计软件&#xff0c;其二次开发能力对提升设计效率具有重要意义。本文介绍一种基于Python的CATIA零件着色工具开发方案&#xff0c;通过PySide6实现GUI交互&#xff0c;结合COM接口操作实现零件着色自动化。该方案成…

Uniapp项目运行到微信小程序、H5、APP等多个平台教程

摘要&#xff1a;Uniapp作为一款基于Vue.js的跨平台开发框架&#xff0c;支持“一次开发&#xff0c;多端部署”。本文将手把手教你如何将Uniapp项目运行到微信小程序、H5、APP等多个平台&#xff0c;并解析常见问题。 一、环境准备 在开始前&#xff0c;请确保已安装以下工具…

ROS分布式部署通信

目录 一、概念 二、设置 ROS 分布式网络 1. 环境要求 2. 主机&#xff08;Master&#xff09;设置 3. 从机&#xff08;节点设备&#xff09;设置 4. 测试是否正常通信 三、进阶启动多从机节点&#xff08;launch&#xff09;。 一、概念 ROS 分布式通信用于在多台计算机…

qt open3dAlpha重建

qt open3dAlpha重建 效果展示二、流程三、代码效果展示 二、流程 创建动作,链接到槽函数,并把动作放置菜单栏 参照前文 三、代码 1、槽函数实现 void on_actionAlpha_triggered();//alpha重建 void MainWindow::

我的三维引擎独立开发之路:坚持与迷茫

今天终于解决了&#xff0c;之前开发的基于threeceisum开发的融合引擎Merge3D,引用threejs版本过低的问题&#xff0c;也算又前进了一步&#xff01; 有人说&#xff0c;直接用最新版本不就行了&#xff0c;哎关键之前版本怎么办哪&#xff0c;很多不兼容性&#xff0c;需要一个…

【ArcGIS】地理坐标系

文章目录 一、坐标系理论体系深度解析1.1 地球形态的数学表达演进史1.1.1 地球曲率的认知变化1.1.2 参考椭球体参数对比表 1.2 地理坐标系的三维密码1.2.1 经纬度的本质1.2.2 大地基准面&#xff08;Datum&#xff09;的奥秘 1.3 投影坐标系&#xff1a;平面世界的诞生1.3.1 投…

数据分析人员需要掌握sql到什么程度?

学习SQL三个层次 熟悉基本的增删改查语句及函数&#xff0c;包括select、where、group by、having、order by、delete、insert、join、update等&#xff0c;可以做日常的取数或简单的分析&#xff08;该水平已经超过90%非IT同事&#xff09;;掌握并熟练使用高阶语法&#xff0…

简洁实用的3个免费wordpress主题

高端大气动态炫酷的免费企业官网wordpress主题 非常简洁的免费wordpress主题&#xff0c;安装简单、设置简单&#xff0c;几分钟就可以搭建好一个wordpress网站。 经典风格的免费wordpress主题 免费下载 https://www.fuyefa.com/wordpress

golang从入门到做牛马:第一篇-我与golang的缘分,go语言简介

还记得2018年的夏天,刚毕业的我不知道该做些什么,于是自学了一周的go语言,想要找一份go语言工作的代码,当时的go还没有go mod来管理依赖包,在北京找了一个月的工作,找到了一个小公司做了后端开发,当然使用go语言开发,带着兴奋劲,年轻身体也好,边努力学习,边工作。 时…

【Python编程】高性能Python Web服务部署架构解析

一、FastAPI 与 Uvicorn/Gunicorn 的协同 1. 开发环境&#xff1a;Uvicorn 直接驱动 作用&#xff1a;Uvicorn 作为 ASGI 服务器&#xff0c;原生支持 FastAPI 的异步特性&#xff0c;提供热重载&#xff08;--reload&#xff09;和高效异步请求处理。 启动命令&#xff1a; u…

Sentinel 笔记

Sentinel 笔记 1 介绍 Sentinel 是阿里开源的分布式系统流量防卫组件&#xff0c;专注于 流量控制、熔断降级、系统保护。 官网&#xff1a;https://sentinelguard.io/zh-cn/index.html wiki&#xff1a;https://github.com/alibaba/Sentinel/wiki 对比同类产品&#xff1…

JQuery 语法 $

jQuery 语法是通过选取 HTML 元素, 并对选取的元素执⾏某些操作 JQuery 选择器 jQuery 中所有选择器都以 $ 开头&#xff1a;$(). JQuery事件 事件由三部分组成: 1. 事件源: 哪个元素触发的 2. 事件类型: 是点击, 选中, 还是修改? 3. 事件处理程序: 进⼀步如何处理. …

算法每日一练 (9)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 算法每日一练 (9)最小路径和题目描述解题思路解题代码…

2025/3/8 第 27 场 蓝桥入门赛 题解

1. 38红包【算法赛】 签到题&#xff1a; 算倍数就行了 #include <bits/stdc.h> using namespace std; int main() {int ans0;for(int i1;i<2025;i){if(i % 3 0)ans;else if(i % 8 0)ans;else if(i % 38 0)ans;}cout<<ans<<endl;return 0; } 2. 祝福…

《白帽子讲 Web 安全》之深入同源策略(万字详解)

目录 引言 一、同源策略基础认知 &#xff08;一&#xff09;定义 &#xff08;二&#xff09;作用 &#xff08;三&#xff09;作用机制详解 二、同源策略的分类 &#xff08;一&#xff09;域名同源策略 &#xff08;二&#xff09;协议同源策略 &#xff08;三&…

基于SpringBoot的商城管理系统(源码+部署教程)

运行环境 数据库&#xff1a;MySql 编译器&#xff1a;Intellij IDEA 前端运行环境&#xff1a;node.js v12.13.0 JAVA版本&#xff1a;JDK 1.8 主要功能 基于Springboot的商城管理系统包含管理端和用户端两个部分&#xff0c;主要功能有&#xff1a; 管理端 首页商品列…

FFmpeg-chapter7和chapter8-使用 FFmpeg 解码视频(原理篇和实站篇)

解码流程如下图 流程&#xff1a;首先&#xff0c;通过 avcodec_alloc_context3(nullptr) 分配一个 AVCodecContext 结构体&#xff0c;然后使用 avcodec_parameters_to_context 将参数复制到上下文中&#xff0c;接着通过 avcodec_find_decoder 查找指定的解码器&#xff0c;并…

【银河麒麟高级服务器操作系统实例】虚拟机桥接网络问题分析及处理

更多银河麒麟操作系统产品及技术讨论&#xff0c;欢迎加入银河麒麟操作系统官方论坛 https://forum.kylinos.cn 了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer…

10 【HarmonyOS NEXT】 仿uv-ui组件开发之Avatar头像组件开发教程(一)

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; 目录 第一篇&#xff1a;Avatar 组件基础概念与设计1. 组件概述2. 接口设计2.1 形状类型定义2.2 尺寸类型定义2.3 组件属性接口 3. 设计原则4. 使用…