闲来无事,写一个命令行版的斗地主 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
iamniconico
V2EX    程序员

闲来无事,写一个命令行版的斗地主

  iamniconico 2018-11-06 21:50:10 +08:00 13179 次点击
这是一个创建于 2582 天前的主题,其中的信息可能已经有所发展或是发生改变。

我将之命名为 ratel,对,没错,平头哥,就是这么霸气(怂

在发这篇主题的时候,ratel 还在开发的最后阶段,已经完成了基本的交互和游戏环节,有待优化,这里放出技术栈:

  • 平台:Java
  • 网络包:netty 4.x
  • 通讯协议:TCP/IP
  • 编解码:粗暴的 jvm 字节码

项目地址:https://github.com/ainilili/ratel

整个流程不难,首先,我需要将客户端和服务端搭起来,netty 提供着简洁的 api,可以快速的部署服务端和客户端,所以这个环节没有任何难度!一个简单的结构图如下:

通讯这一块搞定,接下来要思考的问题就是如何进行互动,这个问题将会引发出更深层的问题?

  • 1、服务端如何存储客户端信息
  • 2、服务端如何识别客户端
  • 3、Poker 出牌的操作怎么通过命令行完成
  • 4、Poker 的类型鉴定与比较
  • 5、命令行如何显示客户端的回合计时
  • 6、客户端掉线
  • 7、其余未知问题

作为一个正在开发的项目,更多的难以解决的疑难杂症还等待着我去发现,本帖也将持续更新至 ratel 完结。

针对于以上问题的思考之后,我决定将数据持久化在内存中(要考虑 jvm 会不会 gc 掉,所以这里使用 final 修饰 static ),服务端抽象出Room - 游戏房间,ClientSide - 客户端信息Poker - 结构这三个最主要的数据结构,网路逐渐变得复杂起来

详细结构如下

Room{ Client{ Poker{ int 唯一标识 int 唯一标识 int 大小 int 状态 int 房间标识 int 花色 map 客户端字典 str 昵称 } list 客户端列表 list 手持牌 int 地主标识 int 状态 int 地主牌 int 类型(农民|地主) struct 上次出手的信息 client 下手 int 上次出手人 client 上手 } } 

每当一个客户端连接时,将会构造一个 Client 对象,分配一个唯一的标识供服务端识别,Room 由客户端建立,并且在此基础之上,其他客户端可以加入已创建且状态未满的房间,当游戏开始后,将会为房间中的所有客户端派发 Poker。

这种流程看似可行,按照这种模式,ratel 由 0 走到了 1

但 ratel 的重点并不止于此,各种问题(网络,安全,用户体验等)还有待解决。

ratel 开发完毕之后,大家工作之余偷偷开心一下,命令行下划划水。

第 1 条附言    2018-11-07 18:45:20 +08:00

根据大家的建议,对ratel的出牌方式做了一些修改,以及新增客户端退出或者异常断开的应对方案。

新的出牌规则: 3 -> 3 4 -> 4 5 -> 5 6 -> 6 7 -> 7 8 -> 8 9 -> 9 10 -> T/t/0 J -> J/j Q -> Q/q K -> K/k A -> A/a/1 2 -> 2 S(小王)-> S/s X(大王)-> X/x

例如如下牌:

Poker: ┌──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐──┐ │3 |5 |6 |6 |7 |8 |8 |9 |9 |9 |10|J |Q |Q |K |K |2 |2 |2 |X | │ | | | | | | | | | | | | | | | | | | | | └──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘──┘ 

出牌的输入是:

56789 t jqk 

则输出是:

Poker: ┌──┐──┐──┐──┐──┐──┐──┐──┐──┐ │5 |6 |7 |8 |9 |10|J |Q |K | │ | | | | | | | | | └──┘──┘──┘──┘──┘──┘──┘──┘──┘ 

56789 t jqk将会被解析成{'5','6','7','8','9','t','j','q','k'}并发往服务端进行命中判断及出牌

最后上图

第 2 条附言    2018-11-12 20:44:41 +08:00
107 条回复    2019-01-14 15:39:11 +08:00
1  2  
cnit
    1
cnit  
   2018-11-06 22:24:00 +08:00
牛皮,我就会 9*9 乘法表
liaojl
    2
liaojl  
   2018-11-06 22:55:23 +08:00 via iPhone
叫地主
iamniconico
    3
iamniconico  
OP
   2018-11-06 23:28:16 +08:00 via Android
@liaojl 我抢
iamniconico
    4
iamniconico  
OP
   2018-11-06 23:29:13 +08:00 via Android
大佬低调了,斗地主都是 javase 的知识
Bigglesworth
    5
Bigglesworth  
   2018-11-06 23:33:46 +08:00
要不起
hourann
    6
hourann  
   2018-11-06 23:36:27 +08:00 via iPhone
牛啊
johnniang
    7
johnniang  
   2018-11-06 23:36:55 +08:00 via Android
厉害厉害
richangfan
    8
richangfan  
   2018-11-06 23:38:37 +08:00 via Android
pass
zaneenaz
    9
zaneenaz  
   2018-11-06 23:40:52 +08:00 via Android
快点啊等到花儿都谢了,,厉害。
iamniconico
    10
iamniconico  
OP
   2018-11-06 23:59:11 +08:00 via Android
四个二带俩王,我摊牌了
lovefantasy
    11
lovefantasy  
   2018-11-07 00:08:34 +08:00 via iPhone
大佬啊
syahd
    12
syahd  
   2018-11-07 00:11:28 +08:00 via Android
声音有不
alakey1989
    13
alakey1989  
   2018-11-07 00:30:43 +08:00
膜拜老哥
catinsides
    14
catinsides  
   2018-11-07 00:39:33 +08:00
自带 bgm 的主题
sinv
    15
sinv  
   2018-11-07 01:07:06 +08:00 via iPhone   1
上次有一把,上家在要地主的过程超时了,应该是掉线了,根据规则他就托管了,我一看直接要了 3 分牌还不错,惊喜来了,下家一看直接放弃抵抗也托管了,然后就变成我斗俩机器人农民。

……

……

干,我特么输了。

被轰了三个炸弹,然后我自己还放了 4 个 A,3 个 2 加一个小鬼憋手里了……

May725
    16
May725  
   2018-11-07 02:14:44 +08:00 via iPhone
哈哈,前两年也有在终端斗地主的想法,用 c 写了一半,就没继续下去了。加油,以后 v2 滑水又多了一种方式
Cbdy
    17
Cbdy  
   2018-11-07 07:10:55 +08:00 via Android
o 家不是要把 jvm 序列化砍了吗?
easylee
    18
easylee  
   2018-11-07 07:44:40 +08:00 via Android
有才!看到标题我还在想怎么显示牌呢。戳 star 去咯。
iamniconico
    19
iamniconico  
OP
   2018-11-07 08:30:15 +08:00 via Android
@Cbdy java10 不清楚,java8 还在
iamniconico
    20
iamniconico  
OP
   2018-11-07 08:34:06 +08:00 via Android
@whwq2012 想法不错,准备试试
wikilike7
    22
wikilike7  
   2018-11-07 09:03:39 +08:00
@sinv 传说中的人打不过电脑,哈哈
artandlol
    23
artandlol  
   2018-11-07 09:07:53 +08:00
什么时候出 releases
hfc
    24
hfc  
   2018-11-07 09:09:49 +08:00
请问,你是在考虑涉及完大部分数据结构、逻辑等之后才开始开发的嘛?
iamniconico
    25
iamniconico  
OP
   2018-11-07 09:10:45 +08:00
@artandlol 预计近两周内
simonguo
    26
simonguo  
   2018-11-07 09:12:26 +08:00 via iPhone
iamniconico
    27
iamniconico  
OP
   2018-11-07 09:13:24 +08:00
@hfc 不全是,有些逻辑和数据结构的缺陷是在开发过程中发现的!
baicheng10
    28
baicheng10  
   2018-11-07 09:13:57 +08:00
插眼
loongwang
    29
loongwang  
   2018-11-07 09:15:38 +08:00
牛皮牛皮
KgM4gLtF0shViDH3
    30
KgM4gLtF0shViDH3  
   2018-11-07 09:16:29 +08:00 via iPhone
花色怎么看啊
iamniconico
    31
iamniconico  
OP
   2018-11-07 09:16:38 +08:00
@Cbdy 看样子是准备要砍了,同时也准备给出弥补?
> To replace the current serialization technology, a small serialization framework would be placed in the platform once records, the Java version of data classes, are supported. The framework could support a graph of records, and developers could plug in a serialization engine of their choice, supporting formats such as JSON or XML, enabling serialization of records in a safe way. But Reinhold cannot yet say which release of Java will have the records capability.
iamniconico
    32
iamniconico  
OP
   2018-11-07 09:17:08 +08:00
@bestkayle 看下最后一张图
reticentfat
    33/div>
reticentfat  
   2018-11-07 09:18:33 +08:00
出了请通知我
CodingDoge
    34
CodingDoge  
   2018-11-07 09:19:10 +08:00
划水新境界
zhang1215
    35
zhang1215  
   2018-11-07 09:26:08 +08:00 via iPhone
花式划水
realkenshinji
    36
realkenshinji  
   2018-11-07 09:28:25 +08:00
没有 test ?我们除了 watch 和 star 你的项目,难道不能 contribute ??
Cbdy
    37
Cbdy  
   2018-11-07 09:36:09 +08:00
@iamniconico
估计是 JDK 官方出一个 JSON 库吧,类似 GSON 或者 FastXML 转正的感觉
参考一下这个:JSR 374 Specification,https://javaee.github.io/jsonp/
Daveedo
    38
Daveedo  
   2018-11-07 09:38:38 +08:00
加油鸭!
iamniconico
    39
iamniconico  
OP
   2018-11-07 09:42:33 +08:00 via Android
@realkenshinji 等 Beta 版本完毕
iamniconico
    40
iamniconico  
OP
   2018-11-07 09:43:39 +08:00 via Android
@Daveedo 感谢支持
iamniconico
    41
iamniconico  
OP
   2018-11-07 09:55:21 +08:00
@Cbdy json 固然好,我之前也考虑用之,不足的一点是 json 无法传递对象类型,接收方在不知道什么类型的情况下,只能转为 map 结构,数据处理起来不太方便,但是我之后还是会换一种编解码方式的,jvm serialization 局限性太高,影响 ratel client 向 python,shell 发展
qwe61655
    42
qwe61655  
   2018-11-07 10:30:42 +08:00 via iPhone
收藏了,
bullettrain1433
    43
bullettrain1433  
   2018-11-07 10:45:15 +08:00
等消息
loveCoding
    44
loveCoding  
   2018-11-07 10:47:54 +08:00
@iamniconico #41 pb 序列化吧
iamniconico
    45
iamniconico  
OP
   2018-11-07 10:47:54 +08:00
ratel 不会让大家久等的
iamniconico
    46
iamniconico  
OP
   2018-11-07 10:48:54 +08:00
@loveCoding pb 好麻烦的,回头试试,不知道适不适合当前模型
lrh3321
    47
lrh3321  
   2018-11-07 10:59:03 +08:00
star 了
zclHIT
    48
zclHIT  
   2018-11-07 11:09:18 +08:00
star 了,坐等 beta test 和 release:)
iamniconico
    49
iamniconico  
OP
   2018-11-07 11:37:33 +08:00
@zclHIT 感谢支持
tianlang1989
    50
tianlang1989  
   2018-11-07 11:44:58 +08:00
能不能不要有牌的样式 就纯数字就行了
划水利器
先 star 为敬
iamniconico
    51
iamniconico  
OP
   2018-11-07 11:56:10 +08:00
@tianlang1989 好主意,我会提供主题的修改入口的
tigerZhang
    52
tigerZhang  
   2018-11-07 11:58:54 +08:00
输入 index ,有点蛋疼,能不能通过光标左右选牌
mason961125
    53
mason961125  
   2018-11-07 12:00:32 +08:00
工作量严重不饱和!(滑稽
leavan
    54
leavan  
   2018-11-07 12:48:28 +08:00
我觉得可以增加选牌的动态效果。比如输入 2,第二张牌就弹出来...比较直观。
iamniconico
    55
iamniconico  
OP
   2018-11-07 13:17:46 +08:00 via Android
@tigerZhang 命令行版的应该做不了,我会思考一下的
iamniconico
    56
iamniconico  
OP
   2018-11-07 13:19:38 +08:00 via Android
@leavan 优化时会改进一下选牌方案,不过弹出来有点不现实,可能需要 gui 的支持
natforum
    57
natforum  
   2018-11-07 13:30:26 +08:00   6
cstj0505
    58
cstj0505  
   2018-11-07 13:32:11 +08:00
@iamniconico 对象类型作为属性放进去,反序列化先去拿到对象类型
iamniconico
    59
iamniconico  
OP
   2018-11-07 13:41:31 +08:00 via Android
@cstj0505 我也这样想过,准备换一种跨平台的方式,优化一下数据传输结构
leavan
    60
leavan  
   2018-11-07 13:45:54 +08:00
@iamniconico 没有不现实吧,你就在字符界面里牌的顶端留一个空白行,弹出来的就把这张牌空白行放到底端就行了
kulove
    61
kulove  
   2018-11-07 13:48:47 +08:00
直接输牌数字也行吧?比如对 2,是 2 2,三带一是,9 9 9 6.。
iamniconico
    62
iamniconico  
OP
   2018-11-07 13:50:59 +08:00
@leavan 命令行打印只能 append,不能 modify 之前已经输出的,所以做不到的
crab
    63
crab  
   2018-11-07 13:51:28 +08:00
给阿姨倒杯卡布奇诺
leavan
    64
leavan  
   2018-11-07 13:52:05 +08:00
最好能做一个光标,不需要鼠标,用类似 Vim 的操作模式,然后空格弹出或弹回某张牌,回车出牌,操作模式会简单很多。对这种精致的小项目很喜欢,已 star,不过不太喜欢这种交互性比较少的出牌方式,期待改进哈~
iamniconico
    65
iamniconico  
OP
   2018-11-07 13:53:25 +08:00
@kulove 主意不错,这样方便选取,感谢
Skifary
    66
Skifary  
   2018-11-07 13:54:14 +08:00
@natforum 莫名其妙的笑到不停
leavan
    67
leavan  
   2018-11-07 13:54:33 +08:00
@iamniconico 我记得是有方法的,你去搜一下某些进度条的实现方式...
iamniconico
    68
iamniconico  
OP
   2018-11-07 13:54:40 +08:00
@leavan 感谢,之后会扩展客户端至其他平台,可能 linux 的 shell 下就可以实现你说的
iamniconico
    69
iamniconico  
OP
   2018-11-07 13:55:38 +08:00
@crab 作者是男的,只是爱好动漫
ID2333
    70
ID2333  
   2018-11-07 14:08:49 +08:00
脑洞真 6,想玩~
NotNil1
    71
NotNil1  
   2018-11-07 14:11:12 +08:00
感觉做个下五子棋的会简单一点哈
1847bell
    72
1847bell  
   2018-11-07 14:14:58 +08:00
我的,看得出来你确实很闲……
NotNil1
    73
NotNil1  
   2018-11-07 14:20:07 +08:00
客户端代码怎么分发呢
MicroPan
    74
MicroPan  
   2018-11-07 14:35:32 +08:00
666,前排关注~
MrUser
    75
MrUser  
   2018-11-07 14:37:40 +08:00
出牌时直接输入要出的牌呗,程序给转成索引,输入 index 太难受了:1=3,3=5,5=6 ……快绕晕了都还怎么记牌
realpg
    76
realpg  
PRO
   2018-11-07 14:47:28 +08:00
@iamniconico #65
其实直接输入牌字母也可以
定义一下两个王是什么符号,比如 A 和 B
然后 1(A)23456789XJQK
问你出啥牌 直接输 111JJ
有效避免图形界面摸鱼困难

另外 提供老板键 一键终端变为编译界面
iamniconico
    77
iamniconico  
OP
   2018-11-07 15:12:06 +08:00
@MrUser 没问题,我也发现这个挺蛋疼的
iamniconico
    78
iamniconico  
OP
   2018-11-07 15:21:36 +08:00
@realpg 我再考虑 10 用什么字母代替,还是说用 0 ?
realpg
    79
realpg  
PRO
   2018-11-07 15:24:57 +08:00
@iamniconico #78
用 X 比较好吧
然后考虑用户习惯兼容个 A=1
iamniconico
    80
iamniconico  
OP
   2018-11-07 15:25:04 +08:00
3 4 5 6 7 8 9 T J Q K A 2 S X
3 4 5 6 7 8 9 10 J Q K A 2 小王 大王
单字母取消空格
iamniconico
    81
iamniconico  
OP
   2018-11-07 15:27:01 +08:00
@realpg 可以的,不过 X 我准备用来表示大王
MrUser
    82
MrUser  
   2018-11-07 15:43:46 +08:00
大王大写 W,小王小写 w
abmin521
    83
abmin521  
   2018-11-07 16:02:01 +08:00
前排预定
Kirscheis
    84
Kirscheis  
   2018-11-07 16:10:24 +08:00 via Android
nb,插个眼
wsstest
    85
wsstest  
   2018-11-07 16:44:49 +08:00
坐等,更新求艾特
q397064399
    86
q397064399  
   2018-11-07 17:07:36 +08:00
现在搞个 Java 依赖是真的多,一下子 springboot 全家桶 全来了
ginux
    87
ginux  
   2018-11-07 17:15:30 +08:00
服务端的高并发考虑是怎么处理的
iamniconico
    88
iamniconico  
OP
   2018-11-07 17:25:55 +08:00
@ginux
高并发危险点主要在于加入房间这一块,不过也简单,对 status 加 volatile 修饰,处理事件时判断一下,创建房间的 id 分配采用 AtomicInteger 生成 id,客户端的 id 则直接采用 channel 的 socketAddress 在服务端的应用端口。

当然还有一些未知的并发问题和安全问题,之后会加强优化
iamniconico
    89
iamniconico  
OP
   2018-11-07 17:27:16 +08:00
@q397064399 是的,应用体积太膨胀了,很多公司都自研框架,当然我也有一套
https://github.com/ainilili/no-framework
q397064399
    90
q397064399  
   2018-11-07 17:30:27 +08:00
@iamniconico #89 还有目前我都没跑起来你那个程序. 把打包上传到 maven 的跟使用的 隔离开来吧 或者写个打包运行的描述也好.
susucoolsama
    91
susucoolsama  
   2018-11-07 18:07:34 +08:00
lei 了,想法不错啊,有木有智障对手的设定。
iamniconico
    92
iamniconico  
OP
   2018-11-07 18:53:56 +08:00 via Android
@q397064399 不好意思,给你发个 demo 地址
https://gitee.com/ainilili/CoffeeTime
iamniconico
    93
iamniconico  
OP
   2018-11-07 18:54:42 +08:00 via Android
@wsstest 已更新,看附言<(_ _)>
dinjufen
    94
dinjufen  
   2018-11-07 19:02:40 +08:00
看完了我想写一个 GUI 的斗地主,但感觉好难
iamniconico
    95
iamniconico  
OP
   2018-11-07 19:19:12 +08:00 via Android
@susucoolsama 说出你的想法
waruqi
    96
waruqi  
   2018-11-07 19:36:59 +08:00 via Android
纯字符?还是用 tui?
iamniconico
    97
iamniconico  
OP
   2018-11-07 19:47:30 +08:00
@waruqi 纯文字
mseasons
    98
mseasons  
   2018-11-07 19:49:28 +08:00
有意思,我去用 Python3 弄一个
lexuskingxx
    99
lexuskingxx  
   2018-11-07 20:16:26 +08:00
牛 13 了
iamniconico
    100
iamniconico  
OP
   2018-11-07 22:38:37 +08:00 via Android
@mseasons 我准备将服务端做成通用的,以后你可以考虑用 py 写客户端<(_ _)>
1  2  
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3372 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 37ms UTC 10:45 PVG 18:45 LAX 02:45 JFK 05:45
Do have faith in what you're doing.
ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86