在线五子棋对决

  • 一. 项目介绍及链接
  • 二. 项目结构设计
    • 项目模块划分
    • 业务处理模块的子模块划分
    • 项目流程图
      • 玩家流程图
      • 服务器流程图
  • 三. 数据管理模块
    • 数据库设计
    • 创建 user_table 类
  • 四. 在线用户管理模块
  • 五. 游戏房间管理模块
    • 游戏房间类实现
    • 游戏房间管理类实现
  • 六. Session 管理模块
    • Session 类实现
    • Session 管理类实现
  • 七. 五子棋对战玩家匹配管理模块
    • 匹配队列类实现
    • 匹配队列管理类实现
  • 八. 整合封装服务器模块
    • 通信接口设计 (Restful风格)
      • 静态资源请求
      • 注册用户请求
      • 用户登录请求
      • 获取用户信息请求
      • 切换 WebSocket 通信协议请求 (进入游戏大厅)
      • 对战匹配请求
      • 停止对战匹配请求
      • 切换 WebSocket 通信协议请求 (进入游戏房间)
      • 下棋请求
      • 聊天请求
    • 服务器类实现
  • 九. 客户端开发
    • 注册页面:register.html
    • 登入页面:login.html
    • 游戏大厅页面:game_hall.html
    • 游戏房间页面:game_room.html

一. 项目介绍及链接

本项目主要实现一个网页版的五子棋对战游戏,其主要支持以下核心功能:

  • 用户管理:实现用户注册,用户登录、获取用户信息、用户天梯分数记录、用户比赛场次记录等。
  • 匹配对战:实现两个玩家在网页端根据天梯分数匹配游戏对手,并进行五子棋游戏对战的功能。
  • 聊天功能:实现两个玩家在下棋的同时可以进行实时聊天的功能。

二. 项目结构设计

项目模块划分

项目模块划分三个大模块来进行:

  • 数据管理模块:基于 MySQL 数据库进行用户数据的管理。
  • 前端界面模块:基于 JS 实现前端页面 (注册,登录,游戏大厅,游戏房间) 的动态控制以及与服务器的通信。
  • 业务处理模块:搭建 WebSocket 服务器与客户端进行通信,接收请求并进行业务处理。

项目要实现的是一个在线五子棋对战服务器,提供用户通过浏览器进行用户注册,登录,以及实时匹配,对战,聊天等功能。而如果要实现这些功能,那么就需要对业务处理模块再次进行细分为多个模块来实现各个功能。

在这里插入图片描述

业务处理模块的子模块划分

  • 网络通信模块:基于 websocketpp 库实现 HTTP 和 WebSocket 服务器的搭建,提供网络通信功能。
  • 会话管理模块:对客户端的连接进行 Cookie 和 Session管理,实现 HTTP 短连接时客户端身份识别功能。
  • 在线管理模块:对进入游戏大厅与游戏房间中用户进行管理,提供用户是否在线以及获取用户连接的功能。
  • 房间管理模块:为匹配成功的用户创建对战房间,提供实时的五子棋对战与聊天业务功能。
  • 用户匹配模块:根据天梯分数不同进行不同层次的玩家匹配,为匹配成功的玩家创建房间并加入房间。

项目流程图

玩家流程图

在这里插入图片描述

服务器流程图

在这里插入图片描述

三. 数据管理模块

数据管理模块主要负责对于数据库中数据进行统一的增删改查管理,其他模块要对数据操作都必须通过数据管理模块完成。

数据库设计

创建 user 表,用来表示用户信息及积分信息。

  • 用户信息:实现登录、注册、游戏对战数据管理等功能 (用户 id,用户名,密码,总场数,胜场数)
  • 积分信息:实现匹配功能 (游戏分数)
drop database if exists gobang;
create database if not exists gobang;
use gobang;
create table if not exists user(id int primary key auto_increment,username varchar(32) unique key not null,password varchar(128) not null,score int,total_count int,win_count int
);

验证数据库是否创建成功:
在这里插入图片描述
在这里插入图片描述

创建 user_table 类

数据库中有可能存在很多张表,每张表中管理的数据又有不同,要进行的数据操作也各不相同,因此我们可以为每一张表中的数据操作都设计一个类,通过类实例化的对象来访问这张数据库表中的数据,这样的话当我们要访问哪张表的时候,使用哪个类实例化的对象即可。

创建 user_table 类,该类的作用是负责通过 MySQL 接口管理用户数据,主要提供了四个方法:

  • select_by_name:根据用户名查找用户信息,用于实现登录功能。
  • insert:新增用户,用户实现注册功能。
  • login:登录验证,并获取完整的用户信息。
  • win:用于给获胜玩家修改分数。
  • lose:用户给失败玩家修改分数。

位于 db.hpp 文件

存在密码不满足要求,也就是密码太简单了,需要设置密码等级,如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四. 在线用户管理模块

  • 管理的两类用户:是对于 “进入游戏大厅” 和 “进入游戏房间” 的用户。
  • 原因:进入游戏大厅和进入游戏房间的用户才会建立 WebSocket 长连接。
  • 管理:将用户 id 和对应的客户端 WebSocket 长链接关联起来。
  • 作用:当一个用户发送了消息 (实时聊天/下棋消息),可以找房间中的其它用户,在在线用户管理模块中,找到这个用户对应的 WebSocket 长连接,然后将消息发送给指定的用户,模块的两个功能如下:
    • 通过用户 id 找到用户的 WebSocket 连接进而实现向指定用户的客户端推送消息,WebSocket 连接关闭时,会自动在在线用户管理模块中删除自己的信息。
    • 可以通过在线用户管理模块判断一个用户是否在线,或者判断用户是否已经掉线。

位于 online.hpp 文件

五. 游戏房间管理模块

游戏房间类实现

  • 对匹配成功的玩家创建房间,建立起一个小范围的玩家之间的关联关系,房间里的一个玩家产生的动作会广播给房间里的其它用户。
  • 因为房间有可能会有很多,因此需要将这些房间管理起来以便于对于房间生命周期的控制。
  • 房间中产生的动作:下棋和聊天都要广播给房间里的其它用户。
  • 实现的两个部分:房间的设计、房间管理的设计。
    • 房间的设计:房间的ID、房间的状态 (正在游戏/游戏结束,决定玩家退出时谁胜利)、房间中用户的数量 (决定了房间什么时候销毁)、白棋玩家ID、黑棋玩家ID、用户信息表的句柄 (当玩家胜利/失败时更新用户数据)、棋盘信息 (二维数组)、在线用户的管理句柄 (广播数据时需要)

位于 room.hpp 文件

游戏房间管理类实现

需要管理的数据:

  • 数据管理模块句柄。
  • 在线用户管理模块句柄。
  • 房间 ID 分配计数器。
  • 用户 ID 映射房间 ID 的哈希表。
  • 房间 ID 映射房间信息的哈希表。
  • 互斥锁。

功能:

  • 创建房间:两个玩家对战匹配完成了,为他们创建一个房间,需要传入两个玩家的用户 ID
  • 查找房间:通过房间 ID 查找房间信息,通过用户 ID 查找所在房间信息。
  • 销毁房间:通过房间 ID 销毁,房间中所有用户都退出了,销毁房间。

位于 room.hpp 文件

六. Session 管理模块

在 Web 开发中,HTTP 协议是一种无状态短链接的协议,这就导致一个客户端连接到服务器上之后,服务器不知道当前的连接对应的是哪个用户,也不知道客户端是否登录成功,这时候为客户端提供所有服务是不合理的。第一次HTTP请求登入使用的是密码,接下来的HTTP请求,由于无状态短链接不知道用户是谁,需要重新登入,这是不合理的。

因此,服务器为每个用户浏览器创建一个会话对象 (Session 对象),注意:一个浏览器独占一个 Session 对象 (默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的 Session 中,当用户使用浏览器访问其它程序时,其它程序可以从用户的 Session 中取出该用户的数据,识别该连接对应的用户,并为用户提供服务。

Session 工作原理:

在这里插入图片描述

使用 Cookie 和 Session 进行 HTTP 在短连接通信的情况下进行用户状态管理,但是这个服务器上管理的 Session 都会有过期时间,超过时间就会将对应的 Session 删除,每次客户端与服务器的通信都需要延长 Session 的过期时间。

Session 类实现

简单设计 session 类,但是 session 对象不能一直存在,这是一种资源泄漏,因此需要使用定时器对每个创建的 session 对象进行定时销毁 (一个客户端连接断开后,一段时间内都没有重新连接则销毁 session),成员对象如下:

  • ssid:会话id
  • uid:用户id
  • status:用户状态 (登入/未登入)
  • timer_ptr:保存当前 session 对应的定时销毁任务。

位于 session.hpp 文件

Session 管理类实现

session 管理:

  • 创建一个新的 session
  • 通过 ssid 获取 session
  • 通过 ssid 判断 session 是否存在。
  • 销毁 session
  • 为 session 设置过期时间,过期后 session 被销毁。

位于 session.hpp 文件

七. 五子棋对战玩家匹配管理模块

匹配队列类实现

五子棋对战的玩家匹配是根据自己的天梯分数进行匹配,而服务器中将玩家天梯分数分为三个档次:

  • 青铜:天梯分数小于 2000 分。
  • 白银:天梯分数介于 2000~3000 分之间。
  • 黄金:天梯分数大于 3000 分。

实现玩家匹配的思想非常简单,为不同的档次设计各自的匹配队列,当一个队列中的玩家数量大于等于2的时候,则意味着同一档次中,有2个及以上的人要进行实战匹配,则出队队列中的前两个用户,相当于队首2个玩家匹配成功,这时候为其创建房间,并将两个用户信息加入房间中。

位于 matcher.hpp 文件

匹配队列管理类实现

匹配管理:

  • 三个不同玩家水平的队列。
  • 三个线程分别对三个队列中的玩家进行匹配。
  • 房间管理模块的句柄:为匹配成功的玩家创建房间。
  • 在线用户管理模块句柄:为匹配成功的玩家添加到在线用户管理中。
  • 数据库管理模块用户表句柄:通过玩家 id 查看玩家的天梯分数,进行玩家匹配。

功能接口:

  • 添加用户到匹配队列。
  • 从匹配队列移除玩家。
  • 线程入口函数:判断指定队列人数是否大于2,出队两个玩家,创建房间,将两个玩家添加到房间中,向两个玩家发送对战匹配成功的消息。

位于 matcher.hpp 文件

八. 整合封装服务器模块

通信接口设计 (Restful风格)

静态资源请求

  • 静态资源页面:在后台服务器上就是个 html/css/js 文件。
    • 注册页面请求。
    • 登入页面请求。
    • 游戏大厅页面请求。
    • 游戏房间页面请求。
  • 静态资源请求的处理:就是将文件中的内容发送给客户端。
# 注册页面请求
请求:GET /register.html HTTP/1.1
响应:
HTTP/1.1 200 OK
Content-Length: xxx
Content-Type: text/htmlregister.html文件内容数据# 登录页面请求
请求:GET /login.html HTTP/1.1
# 大厅页面请求
请求:GET /game_hall.html HTTP/1.1
# 房间页面请求
请求:GET /game_room.html HTTP/1.1

注册用户请求

// 请求
POST /reg HTTP/1.1
Content-Type: application/json{"username":"zhangsan", "password":"123456"}// 成功时的响应
HTTP/1.1 200 OK
Content-Type: application/json{"result":true, "reason": "注册用户成功"}// 失败时的响应 
HTTP/1.1 400 Bad Request
Content-Type: application/json{"result":false, "reason": "用户名已经被占用"}

用户登录请求

// 请求
POST /login HTTP/1.1
Content-Type: application/json{"username":"zhangsan", "password":"123456"}// 成功时的响应
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: 123456{"result":true, "reason": "登入成功"}// 失败时的响应 
HTTP/1.1 400 Bad Request
Content-Type: application/json{"result":false, "reason": "用户名/密码错误"}

获取用户信息请求

// 请求
POST /info HTTP/1.1
Content-Type: application/json// 成功时的响应
HTTP/1.1 200 OK
Content-Type: application/json{"username":"xzy", "score":1000, "total_count":4, "win_count":2}// 失败时的响应 
HTTP/1.1 400 Bad Request
Content-Type: application/json{"result":false, "reason": "找不到用户信息信息, 请重新登入"}

切换 WebSocket 通信协议请求 (进入游戏大厅)

// 成功响应
{"optype": "hall_ready","result": true;
}
// 失败响应
{"optype": "hall_ready","reason": "玩家重复登入""result": false;
}

对战匹配请求

// 客户端:开始匹配玩家的信息
{"optype": "match_start","uid": 1
}// 服务器:开始匹配玩家正确的信息
{"optype": "match_start","result": true
}
// 服务器:开始匹配玩家错误的信息
{"optype": "match_start","result": false,"reason": "具体原因...."
}// 服务器:匹配玩家成功的信息
{"optype": "match_success","result": true
}

停止对战匹配请求

// 客户端:停止匹配玩家的信息
{"optype": "match_stop""uid": 1
}// 服务器:停止匹配玩家正确的信息
{"optype": "match_stop","result": true
}
// 服务器:停止匹配玩家错误的信息
{"optype": "match_stop","result": false,"reason": "具体原因...."
}

切换 WebSocket 通信协议请求 (进入游戏房间)

// 成功响应
{"optype": "room_ready","room_id": 222,"uid": 1,"white_id": 1,"black_id": 2,"result": true;
}
// 失败响应
{"optype": "room_ready","reason": "玩家重复登入""result": false;
}

下棋请求

// 请求
{"optype": "put_chess","room_id": 222,"uid": 1, // 下棋用户的id"row": 3,"col": 2
}// 失败响应
{"optype": "put_chess","result": false"reason": "走棋失败具体原因...."
}
// 成功响应
{"optype": "put_chess","room_id": 222,"uid": 1, // 下棋用户的id"row": 3,"col": 2,"result": true,"reason": "五星连珠,战无敌","winner": 0 // 0 代表没有分出胜负,具体的数字代表获胜用户的id
}

聊天请求

// 请求
{"optype": "chat","room_id": 222,"uid": 1,"message": "快点吧,等的花都谢了"
}// 失败响应
{"optype": "chat","result": false"reason": "消息中包含敏感词汇,无法发送"
}
// 成功响应
{"optype": "chat","result": true,"room_id": 222,"uid": 1,"message": "快点吧,等的花都谢了"
}

服务器类实现

服务器模块:对当前所实现的所有模块的一个整合,并进行服务器搭建的一个模块,最终封装实现出一个 gobang_server 的服务器模块类,向外提供搭建五子棋对战服务器的接口,通过实例化的对象可以简便的完成服务器的搭建。

服务器的整合实现:

  • 网络通信接口的设计:
    • 收到一个什么格式的数据,代表了什么样的请求,应该给与什么样的业务处理以及响应。
  • 开始搭建服务器:
    • 搭建 websocket 服务器,实现网络通信。
    • 针对各种不同的请求进行不同的业务处理。

客户端所存在的请求:

  • 客户端从服务器获取一个 “注册” 页面。
  • 客户端给服务器发送一个 “注册” 请求 (提交用户名和密码)
  • 客户端从服务器获取一个 “登入” 页面。
  • 客户端给服务器发送一个 “登入” 请求 (提交用户名和密码)
  • 客户端从服务器获取一个 “游戏大厅” 页面。
  • 客户端给服务器发送一个 “获取个人信息” 请求 (展示个人信息)
  • 客户端给服务器发送一个 “切换 WebSocket 通信协议” 的请求 (建立游戏大厅长连接)
  • 客户端给服务器发送一个 “对战匹配” 的请求 (匹配玩家)
  • 客户端给服务器发送一个 “停止对战匹配” 的请求 (停止匹配玩家)
  • 客户端从服务器获取一个 “游戏房间” 页面。
  • 客户端给服务器发送一个 “切换 WebSocket 通信协议” 的请求 (建立游戏房间长连接)
  • 客户端给服务器发送一个 “下棋” 的请求 (下棋)
  • 客户端给服务器发送一个 “聊天” 的请求 (聊天)
  • 客户端从服务器获取一个 “游戏大厅” 页面 (游戏结束返回游戏大厅)

九. 客户端开发

注册页面:register.html

通过 43.143.27.66:8080/register.html 访问该网页时,返回给客户端一个注册界面,用户输入用户名密码后点击按钮,自动通过 ajax 向后台发送用户注册请求,并将用户名和密码存储到数据库中,最后直接跳转到登入页面。

在这里插入图片描述

登入页面:login.html

通过 43.143.27.66:8080/login.html 访问该网页时,返回给客户端一个登入界面,用户输入用户名密码后点击按钮,自动通过 ajax 向后台发送用户登入请求,并直接跳转到游戏大厅页面。

在这里插入图片描述

游戏大厅页面:game_hall.html

进入游戏大厅 websocket 长连接页面后,通过点击开始匹配按钮,网页向服务器发送对战匹配请求,再次点击按钮,网页向服务器发送取消对战匹配请求,当存在两个人匹配对战的话,则玩家匹配成功,直接跳转到游戏房间页面。

在这里插入图片描述

游戏房间页面:game_room.html

进入游戏房间 websocket 长连接页面后,就可以开始实时下棋和聊天了。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/97977.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97977.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/97977.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重构导航之核:高德地图的深度学习架构解析 导论:从数字化世界到可计算世界

导论:从数字化世界到可计算世界 数字地图的演进,本质上是一场关于“世界可计算性”的持续探索。第一代地图的核心任务是数字化转录(Digital Transcription),它成功地将物理世界的静态元素——道路、建筑、兴趣点&#…

逻辑回归(sigmoid函数、混淆矩阵、精确率召回率F1)

目录 一、概述 1、逻辑回归 2、激活函数 sigmoid函数 3、最大似然估计 二、逻辑回归 1、原理 2、损失函数 3、代码 三、混淆矩阵 1、定义 2、举例 3、代码 四、分类评估方法 1、精确率(Precision) 2、召回率(Recall) 3、F1&#…

Redis底层实现原理之五大基础结构

文章目录1. 基础结构和编码类型2. 编码类型和数据结构实现2.1 字符串(String)2.2 压缩列表(listpack)2.3 哈希表(hashtable)2.4 快速列表(quicklist)2.5 整数集合(intset…

火山引擎数据智能体DataAgent总结分享

数据的冰山:看得见的资产与看不见的鸿沟 这张图片用“冰山”类比的方式展示了数据资产管理中的可见与不可见问题,并突出了数据利用的核心挑战与潜在陷阱。 1. 冰山之上的“看得见的资产” 内容:数据库、报表、指标等结构化、显性的数据资源。 核心挑战: 需要从“采集存储”…

100种高级数据结构 (速查表)

一、 基础结构的扩展与组合 (Advanced Linear Structures) 这些结构在数组、链表、队列、栈等基础结构上增加了特定功能或约束。双端队列 (Deque - Double-Ended Queue) 介绍:允许在队列的前后两端都进行插入和删除操作的线性结构。应用场景:工作窃取算法…

一个开源的企业官网简介

简介一个完美的企业官网系统,支持手机端和电脑端展示企业风采,还可以展示企业产品/企业新闻资讯等等.普通用户PC端展示普通用户手机端展示管理后台

TCP实现线程池竞争任务

服务端&#xff1a;#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<netinet/ip.h> #include<strings.h> #include<unistd.h> #include<ctype.h> #include<arpa/inet.h&…

Redis C++ 实现笔记(F篇)

Implementing Redis in C : F Redis C 实现笔记&#xff08;F篇&#xff09; 前言 本章代码及思路均来自Build Your Own Redis with C/C 本文章只阐述我的理解想法&#xff0c;以及需要注意的地方。 本文章为续<<Implementing Redis in C : E>>所以本文章不再…

finally 与 return的执行顺序

一、第一次试验public static void main(String[] args) throws InterruptedException {System.out.println(aaa(null));}private static StringBuilder aaa(Integer i) throws InterruptedException {StringBuilder sb new StringBuilder();try {i.toString();return sb;} ca…

Git安装教程

简介 Git 是目前全球最流行的分布式版本控制系统&#xff08;Distributed Version Control System, DVCS&#xff09;&#xff0c;核心作用是追踪文件修改历史、支持多人协同开发&#xff0c;并能高效管理代码&#xff08;或任何文本类文件&#xff09;的版本迭代。它由 Linux…

Linux安装RTL8821CE无线网卡驱动

1. 查看网卡芯片$ lspci | grep Net 01:00.0 Network controller: Realtek Semiconductor Co., Ltd. RTL8821CE 802.11ac PCIe Wireless Network Adapter2. 预备配套sudo apt install -y dkms git3. 下载驱动并安装git clone https://github.com/tomaspinho/rtl8821ce.git cd r…

vue3存储/获取本地或会话存储,封装存储工具,结合pina使用存储

目录 一、基本用法&#xff08;原生 API&#xff09; 1. 存储数据 2. 获取数据 3. 删除数据 二、Vue3 中封装成工具函数&#xff08;推荐&#xff09; 三、以上工具函数在 Vue3 组件中使用 1. 在选项式 API 中使用 2. 在组合式 API&#xff08;setup 语法糖&#xff09;…

【Flink】DataStream API:基本转换算子、聚合算子

目录基本转换算子映射&#xff08;map&#xff09;过滤&#xff08;filter&#xff09;扁平映射聚合算子按键分区&#xff08;keyBy&#xff09;简单聚合&#xff08;sum/min/max/minBy/maxBy&#xff09;规约聚合&#xff08;reduce&#xff09;基本转换算子 有如下POJO类用来…

从淘宝推荐到微信搜索:查找算法如何支撑亿级用户——动画可视化

本篇技术博文摘要 &#x1f31f; 本文通过动画可视化深入解析数据结构中的核心查找算法&#xff0c;从基础概念到高阶应用&#xff0c;全面覆盖顺序查找、折半查找、分块查找、B树/B树及散列查找的核心原理与实现细节。文章以动态演示为核心工具&#xff0c;直观展现算法执行过…

图像正向扭曲反向扭曲

在图像处理领域&#xff0c;正向扭曲&#xff08;Forward Warping&#xff09;和反向扭曲&#xff08;Backward Warping&#xff09;是两种核心的图像坐标映射与像素重采样技术&#xff0c;核心区别在于“像素映射的方向”——是从“原始图像”到“目标图像”&#xff0c;还是从…

【C语言】 第三课 函数与栈帧机制详解

1 函数的基本概念 在C语言中&#xff0c;函数是程序的基本执行单元。一个函数的定义包括返回类型、函数名、参数列表和函数体。例如&#xff1a; int add(int x, int y) { // 函数定义int z x y;return z; }在使用函数前&#xff0c;通常需要声明&#xff08; declaration&am…

多个大体积PDF文件怎么按数量批量拆分成多个单独文件

在现代社会中&#xff0c;电子文档在我们的身边无所不在&#xff0c;而PDF文件时我们日常接触非常多的文档类型之一。PDF由于格式稳定、兼容性好&#xff0c;因此经常被用于各行各业。但是&#xff0c;我们平时在制作或搜集PDF文件时&#xff0c;文件太大&#xff0c;传输和分享…

ansible-角色

角色 一、利用角色构造ansible playbook 随着开发更多的playbook&#xff0c;会发现有很多机会重复利用以前编写的playbook中的代码。或许&#xff0c;一个用于为某一应用配置MySQL数据库的play可以改变用途。通过利用不同的主机名、密码和用户来为另一个应用配置MySQL数据库。…

git命令行打patch

在 Git 里打 patch&#xff08;补丁&#xff09;其实就是把某些提交的改动导出来&#xff0c;生成一个 .patch 文件&#xff0c;方便别人用 git apply 或 git am 打进代码里。&#x1f539; 常用方式1. 基于提交导出 patch导出最近一次提交&#xff1a;git format-patch -1 HEA…

文华财经多空提示指标公式 变色K线多空明确指标 文华wh6赢顺多空买卖提示指标

XX:240C;YY:MA(C,1);A1:POW(XX,2)/360-POW(YY,2)/260;A5:EMA2(EMA2(A1,20),5),LINETHICK2;A6:A5*0.9999,COLORSTICK;A20:EMA2(EMA2(A5,20),5),LINETHICK2;A60:EMA2(EMA2(A20,20),5),LINETHICK2;支撑:HHV(A5,30),COLORRED;天数:BARSSINCE(A5HHV(A5,0));YL:REF(A5,1)2.79-天数*0.…