我们一起学程序-五子棋

作者: 叫练

前言


  小时候很喜欢玩电视上自带的积木游戏,那时候电子产品也不少,小学就认识了low和high两个单词,因此攒了零花钱搞到了高级版的游戏–小霸王学习机 ,说错了,是"游戏机”,特别是一放暑假,插个小霸王游戏机卡就能开始撸几把魂斗罗,坦克大战,比夏天吃根冰棍可香多了,那时候没有空调,不惧严寒酷暑的们在这些小游戏的陪伴下玩的不亦乐乎,陪们度过一个又一个快乐冬夏,下图为证,有没有很熟悉的赶脚!

image.png

  看着这些画面,带着学习的心情的出发了,开始们的五子棋大战;砍柴磨刀互相不耽误,先看看实现的网络版本效果吧,当然为了你看着舒服,体验的开心,博主花了些功夫大致在客户端(手机)做了适配,不至于你屏幕溢出,你爽爽大家爽;简单说明下本人主攻后端Java,前端不深入,页面效果将就看看哈!

体验效果


  看看你想下盘棋,你需要做什么,首先你需要抢个位子啊【媳妇说这个举手的小人太丑了,能换个动漫吗,说对漫画没啥审美,但是对另一半要求很高,所以们在一起了,嘻,这个求生欲也特强了吧!】,然后你就到棋盘页面了,点击举手坐下,你就开始等,有人来了也举手了你们就可以开始了,就像你去棋牌室打牌一模一样,如果硬说有啥区别,咱们这是学习,他们那是赌博,好了,好了,扯远了,简单介绍下,五子棋的规则大家都懂,你五个棋子连成一条线就胜利了,一攻一防,胜率就看你的手段了,不知道你有没有发现,博主为了不让你有更多的束缚,连账户密码都默认为你输出了,同时为了让程序不是那么单调,下棋还能发声,还实现了查看复盘,和在线聊天,聊天IP内容已经存档,自动过滤色情信息;博主为你们担心的事情都精心考虑设计实现,是不是暖男你们说了算!下面两个动画分别是"准备过程"和"下棋过程”;

体验地址:棋子棋体验地址,有些功能可能有不完善的地方,功能也不特别太全,但基本功能应该是满足了,如果没有人陪你玩,你可以开两个客户端体验或call me,一毛钱一把,欢迎来战 ,如果有强迫症的或是有good idea,想要加入们的修改的童鞋,欢迎留言加入们,让们共同成长。

游戏准备过程

下棋过程

关键技术简介


  其实在开发完这个小游戏后,回顾总了下,没有啥高精尖的技术,前端页面完全是html,css(样式),js+jquery(脚本语言),像"举手准备”,“画棋盘"等都是用到的这些前端技术,大家看到的淘宝页面基本都是基于这些技术来完成的;后端用到的技术是websocket,很多面试官会问到websocket和http的区别,博主简单介绍下,它是一个双向通信的协议,这个不难理解,如果你打了,也可以还手,不像http,它是单向的,比如你打了,就只能默默忍着,顶多就能回骂一句,绝不能还手,相信在这个人人平等的社会,你肯定喜欢websocket,他帮助们实现了在线聊天,咿!怎么突然想到了QQ,感觉像腾讯的高级攻城狮一样牛掰;当然了,作为一个Java攻城狮,必须要理解的就是线程啦,们里面的"查看复盘"的功能就用到了Java读写文件和多线程技术;是不是小小的程序还有大大的奥妙;好啦,就到这里吧,明不明白都不要紧,因为们后面详细一点你就能明白了!

总结下:前端css+js实现画页面和交互,后台Java+websocket实现存储和通信 相信有一点基础的你,肯定也能实现这个程序;在实际开发中,前后端是两个不同的岗位,前后端各司其职,当然有些小公司要求程序员都要会,所以有些时候在挑选公司时擦眼睛不是眼里进了沙子,是为了看的更清楚!

从无到有思路


  这个程序博主一个人断断续续差不多花了两个礼拜完成的,当然你肯定怀疑是否有一气呵成的能力,可以肯定的回答你,你的怀疑是对的,因为也是人,不能忘了吃饭和睡觉;最原始最原始的要追忆到两周前,下图是最初的版本,这是在idea控制台输出的,是的,你没有看错,五子棋程序实现了,“五子棋连线成功,恭喜你赢了”,说实话,打出这句话,连这么没有追求的人都觉得他太枯燥了,生活它需要仪式感,你上面看到的页面都是在仪式感基础上添加的;新事物,全新的页面,至少感受到了程序至少还有那么一丝丝的美,是的,至少此刻沦陷了,学程序们要有意识的去追求一下她的美,不然他会教你从入门到放弃 ;

image.png

  慢慢的,“仪式感"让走向一条不归路 ,首先需要解决的问题,就是棋盘的问题,现实中有木头刻制的棋盘,网络上可以用代码生成的棋盘,现实的中棋盘有她独特的美感,网络中这些棋盘他就更灵活了,每个方格,棋盘大小都是可调整的,成本微乎其微,有了棋盘那就需要把棋子落在棋盘上,这些可以用css+js画出来的,还不用浪费木材,落子了每次都需要判断这次走的这步棋是否连成了一个五子,如果连成五子就赢了,如果没有就换对手下棋,每一步过程组合在一起就组成了棋盘,电脑有超强计算和存储能力,人脑也有,但电脑没有人脑灵活,人工智能尚在起步,未来可期;

下完棋后想看下过程于是就有了"复盘回看"功能;下棋要沟通就有了网络聊天室;棋牌室永远不止一个座位,于是就需要支持多个座位,多人同时娱乐;当然你也可以说自己水平不够,需要人机对战,提升经验,或者有人说游戏赢了或输了要有对应的奖惩机制,没错,你能想到的,有兴趣的事情你都可以动手实现,兴趣或者学习目的都好,总比无聊消磨时光来的更有意义;这就是从无到有一步步构建出来的程序 。欢迎和一起讨论学习!【公众号:叫练

代码实现过程


1.棋盘实现

  棋盘是用table表格画的,像你看到博主实现的这个表格,横轴是26,纵轴是23,每个格子的大小都是40像素,不信你话可以量量哈,前端循环放置在div中就可以了,格子像素是固定的,因为们需要通过像素计算最终得到坐标,下棋过程就是通过点击格子,把白或黑的棋子(图片)放置上去,那怎么计算坐标呢?你需要计算相对于棋盘点击的坐标,也就是下图们的红圈 ,们知道棋盘表格(table)是整个页面(body)中的一部分,js中click事件可以获取到全局事件event变量,所以能够获取到相对于body的event.left,event.top,再减去table的top,left就可以得到表格具体的像素,table的top left可以通过getBoundingClientRect 方法获取;table的像素再除40px就是具体坐标了;大家在实现的时候还需要注意一个有效点击 ,只有在这个范围内才算有效范围,像们这个程序,是以坐标点15px都算有效点击;那么无效点击范围在16px-25px,也就是点击格子中间是无效的。举个例子吧,比如们现在算出表格具体坐标是top:130px,left:70px,先计算这个坐标是否有效,130%40=10, 70%40=30,都不在无效范围内,说明是有效坐标,们这里横纵坐标分别记为xy,那么具体坐标就y=130/40 =3,x=70/40=1,此时x坐标需要加上1,因为x坐标大于25,靠右,需要再除的基础上加上1,所以最终横纵坐标为(2,3),这个过程涉及到一点小算法,你对着下图再思考下这个计算过程看看是否有不明白的,万事开头难!下面的图方便大家理解!

image.png

2.计算棋子是否获胜

  在写这个之前先给大家爆料下,没思考听到这个问题前呼吸都是混乱的,说实话真的好难啊,什么?还要实现,TM不想学习了!嘿,兄嘚,别怕,有在呢!怕解决不了问题,牵着你,们在一起吧!

image.png

  兵马未动,图表先行,给大家先上个图吧,其实就四条线;一个“米” 子,会写米字,问题就解决了, 也就是说每次落子后需要逐一判断这4条线上是否有五个相同颜色的棋子,其实对应Java程序程序来说,他是用一个二维数组形式来存储的棋盘数据的,们以红色为中心点,其实这个过程就是通过方式1(纵向):先向上找,找红色的棋子,找到一个计数器就+1,不是红色的棋子直接break退出,同理向下找,找红色的棋子,找到一个计数器就+1,不是红色的棋子直接break退出,最终如果满足5个相同颜色的棋就算成功,也不用再通过方式2,3,4找了,如果方式1不满足条件,方式1就结束了,接下来就按方式2(左斜)查找,也就是同样的套路,一直到方式4为止,哈哈,是不是有点感觉了!先以方式1查找举例子吧,对二维数组来说,以先上找后下找,上找:横坐标不变,纵坐标递减;下找:横坐标不变,纵坐标递增。再按方式三举例,先右上后左下查找,右上:横坐标+1,纵坐标-1,左下:横坐标-1,纵坐标+1;你看看原理还是很简单吧!isSuccess 方法是按方式1查找的。

1599037247(1).jpg

public boolean isSuccess(int x, int y,int color,int[][] oriData)  {
        boolean flag = false; int count = 0; //(2)上-下,左-右,左上-右下,右上-左下; 4种方式 // 方式1 :上-下 x相同,y不同 //上 纵坐标递减; for (int i = y-1; i>-1; i--) { //判断同一颜色的子; if (oriData[x][i] != color) break; count++; } //下 纵坐标递增; for (int i = y+1; i<GameManager.Y; i++) { //判断同一颜色的子; if (oriData[x][i] != color) break; count++; } //重置 if(count >=4) return true; else count = 0; // 方式2 // 方式3 // 方式4 return falg }

3.网络聊天室实现

不知道你有没有发现,你有时候浏览网页会弹出一些广告页,上面时长会自动弹出人工客服窗口,不需要登陆什么的,你们能在线对话,其实这个功能可能就是websocket实现的,们说过,websocket是双向的,在实现上和http一样,都是基于TCP实现的应用层协议,除了http是单向,ws是双向的。在用法上http是以http://头请求的短连接,是一次请求,ws是以ws://请求头的长连接,们在"五子棋游戏大厅"点击位置进入棋盘页面其实就是一个连接过程,前端通过ws://ip:port/xx形式连接后台暴露的地址,会触发后台onOpen函数,每个连接在后台都是以不同Session对象存在,通过 Session 可以获取每个每个网页的session 的id ,就可以调用 sendText 方法 ,通知对应的浏览器,这样一个过程就实现了浏览器和后台交互;比如们的对话,每个桌子是保存两个用户(Session),当其中某一个用户发送了某段话,会通知对应的浏览器,另一个用户也会把这句话发给对应的浏览器,所以你看到的两个页面都出现了一样的文字,其实原理还是比较简单的。

们画个图来理解下这个过程。

image.png websocket通信过程

下面这两个段代码是websocket客户端和服务器交互的代码,只贴了部分源码,限于篇幅有限,如果需要完整代码,下方【环境部署部分 】给了源码下载压缩包

webSocket = new WebSocket("ws://"+serverIp+"/game/game/"+table+"/" + nickname);
 1 /**
 2  * websocket服务连接入口
 3  * update by jiaolian 2020 8 12
 4  * 公众号:叫练
 5  */
 6 @ServerEndpoint("/game//{nickname}")
 7 public class WebserviceSession { 8  @OnOpen 9 public void onOpen(@PathParam("table") String tableName,@PathParam("nickname") String nickname, Session session) throws IOException {} 10  @OnMessage 11 public void onMessage(@PathParam("table") String tableName,String message, @PathParam("nickname") String nickname) {} 12  @OnClose 13 public void onClose(Session session,@PathParam("nickname") String nickname,@PathParam("table") String roomName) {} 14 }

4.查看复盘实现

这个功能是线程+文件实现的,既然需要查看复盘,那说明们需要先写复盘,复盘其实就是写一个文件,查看复盘就是读文件,在一局棋中,每次落子就会写一行数据,每行数据会记录棋子的x,y坐标和棋子的颜色,按空格隔开 ,们代码中Table【棋盘】对象保存color约定1为黑子,2为白子,当一局游戏结束后,查看复盘会生成对应一个线程,以每秒一行的速度读取保存在服务器的文件,文件名以"桌号+时间戳.wzq"命名,如:1_2020_09_01_15_45_15.wzq,每行数据读取出来后,会单独发给们的User【Session】,浏览器收到通知后渲染页面就完事了;贴一段简单的代码;其中Table类是用来保存棋盘的;User类是用来保存用户信息的;finishReplayer () 方法是读取文件,sendTextSingle() 方法负责给浏览器发送信息;可以参考上面websocket通信过程;

/**
 * @author :jiaolian
 * @date :Created in 2020-08-14 13:30
 * @description:桌号
 * @modified By:
 * 公众号:叫练
 */
public class Table {
    //保存用户信息
 @JsonIgnore private List<User> userList = new OnWriteArrayList<>(); //桌号名 private String name; //默认棋盘 private int[][] oriData = new int[GameManager.X][GameManager.Y]; //约定:1 黑子 2 白子 -1 无子(页面选棋子) 此时应该走动棋子颜色; //棋子颜色<默认黑子先走> private int color = CONST.CHEER_COLOR.BLACK; //对应的文件名<复盘> 最近一次 private String fileName; //桌子状态; 已开始/未开始 private int tableStatus; private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); private List<User> tempList; }
/**
 * @author :jiaolian
 * @date :Created in 2020-08-14 13:56
 * @description:在线用户
 * @modified By:
 * 公众号:叫练
 */
public class User {
    //websocket sessoin;
    private Session session; private String nickname; //棋手是否已准备; private boolean isPrepared; //用户的复盘线程;key规则:桌号_用户名,为什么这么设计? 因为一个用户可能在多个桌子上同时游戏; //val值设计成Boolean,主要是查询对应桌数用户线程是否执行完毕; 如果有疑问,请咨询叫练; //设计这个目的主要实现:不能同时多次点击复盘; private ConcurrentHashMap<String,Boolean> reviewThreadMap = new ConcurrentHashMap(); }
public void finishReplayer(Table table,User user) {
        inputStream = new FileInputStream(table.getFileName()); inputStreamReader = new InputStreamReader(inputStream); bufferedReader = new BufferedReader(inputStreamReader); while ((res = bufferedReader.readLine()) != null) { System.out.println(res); //谁点谁看  GameManager.sendTextSingle(table,res,user); try { //休眠一秒 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }

遇到问题


  • springMVC jackson返回实体对象通过不能修改后不能正常转化为json传递到前端?

在实现上述功能过程中,说没遇到问题是不可能的,很多都是业务的bug,就不详细介绍了 ,该五子棋程序没有写数据库,都是在内存处理的,其中渲染首页桌子页面的数据都是从后台一个大的List获取的,如果List数据在修改后,再刷新首页,会出现栈溢出,不能写json的情况,目前处理的方式是用了@JsonIgnore 忽略对象返回到前端,把这个List的User对象重新复制了一份到一个新的List,查看了源码,暂时还没找到原因!找到原因的童鞋欢迎下方留言,一起探讨下!感谢了

Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind

源码下载地址及注意事项

  • JDK1.8及以上;有lamda表达式需要支持
  • maven;需要下载websocket包
  • 源码📎fivegame.rar{AMsqj}

总结


整个程序实现下来还是有种莫名的成就感,博主希望把这个过程分享给你,如果你能有所收获,对来说就是最大的宽慰,写作的目的也在于此,文章创作过程中涉及的内容和知识点可能不太全,后续如果有需要会适当补充和删改,这篇文章是博主的技术处女作 ,如果有疑问请下方留言哦!们可以一起讨论交流学习心得;是【公众号:叫练,边叫边练; 喜欢的可以加关注哦!再次感谢大家的观看支持。 以上的文字,厚起脸皮问你要一颗小红心,你会给吗? 体验地址:五子棋体验,再发一遍。

[] ) [](top

原文创作:叫练

原文链接:https://www.cnblogs.com/jiaolian/p/13612444.html

文章列表

更多推荐

更多
  • AWS自动化机器学习-十一、MLSDLC 的持续集成、部署和训练 技术要求,编纂持续集成阶段,管理持续部署阶段,管理持续训练,延伸,构建集成工件,构建测试工件,构建生产工件,自动化持续集成流程,回顾构建阶段,回顾测试阶段,审查部署和维护阶段,回顾应用用户体验,创建新的鲍鱼调查数据,回顾持续训练流程,清
    Apache CN

  • AWS自动化机器学习-六、使用 AWS 步骤函数自动化机器学习过程 技术要求,介绍 AWS 步骤功能,使用 Step 函数 Data Science SDK for CI/CD,建立 CI/CD 渠道资源,创建状态机,解决状态机的复杂性,更新开发环境,创建管道工件库,构建管道应用构件,部署 CI/CD
    Apache CN

  • AWS自动化机器学习-第三部分:优化以源代码为中心的自动化机器学习方法 本节将向您介绍整体 CI/CD 流程的局限性,以及如何将 ML 从业者的角色进一步整合到管道构建流程中。本节还将介绍这种角色集成如何简化自动化过程,并通过向您介绍 AWS Step 函数向您展示一种优化的方法。本节包括以下章节:
    Apache CN

  • AWS自动化机器学习-一、AWS 上的自动化机器学习入门 技术要求,洗钱流程概述,洗钱过程的复杂性,端到端 ML 流程示例,AWS 如何使 ML 开发和部署过程更容易自动化,介绍 ACME 渔业物流,ML 的情况,从数据中获得洞察力,建立正确的模型,训练模型,评估训练好的模型,探索可能的后续步
    Apache CN

  • AWS自动化机器学习-二、使用 SageMaker 自动驾驶器自动化机器学习模型开发 技术要求,介绍 AWS AI 和 ML 前景,SageMaker 自动驾驶器概述,利用 SageMaker 自动驾驶器克服自动化挑战,使用 SageMaker SDK 自动化 ML 实验,SageMaker Studio 入门,准备实验
    Apache CN

  • AWS自动化机器学习-四、机器学习的持续集成和持续交(CI/CD) 四、机器学习的持续集成和持续交CI/CD技术要求,介绍 CI/CD 方法,通过 CI/CD 实现 ML 自动化,在 AWS 上创建 CI/CD 管道,介绍 CI/CD 的 CI 部分,介绍 CI/CD 的 CD 部分,结束循环,采取以部
    Apache CN

  • AWS自动化机器学习-九、使用 Amazon Managed Workflows 为 Apache AirFlow 构建 ML 工作流 技术要求,开发以数据为中心的工作流程,创建合成鲍鱼调查数据,执行以数据为中心的工作流程,构建和单元测试数据 ETL 工件,构建气流 DAG,清理, 在前面的年龄计算器示例中,我们了解了如何通过 ML 从业者和开发人员团队之间的跨职能
    Apache CN

  • AWS自动化机器学习-七、使用 AWS 步骤函数构建 ML 工作流 技术要求,构建状态机工作流,执行集成测试,监控管道进度,设置服务权限,创建 ML 工作流程, 在本章中,我们将从第六章中的 [处继续,使用 AWS 步骤函数自动化机器学习过程。您将从那一章中回忆起,我们正在努力实现的主要目标是简化
    Apache CN

  • AWS自动化机器学习-八、使用 Apache Airflow 实现机器学习过程的自动化 技术要求,介绍阿帕奇气流,介绍亚马逊 MWAA,利用气流处理鲍鱼数据集,配置 MWAA 系统的先决条件,配置 MWAA 环境, 当建立一个 ML 模型时,有一个所有 ML 从业者都知道的基本原则;也就是说,最大似然模型只有在数据被训练时
    Apache CN

  • AWS自动化机器学习-五、自动化 ML 模型的持续部署 技术要求,部署 CI/CD 管道,构建 ML 模型工件,执行自动化 ML 模型部署,整理管道结构,创建 CDK 应用,部署管道应用,查看建模文件,审查申请文件,查看模型服务文件,查看容器构建文件,提交 ML 工件,清理, 在 [第 4
    Apache CN

  • 近期文章

    更多
    文章目录

      推荐作者

      更多