网页联机红心大战开发日志
玩windows红心大战的时候突然想到可以写一个 打开微软大战代码开干
确定框架
首先确定需求和技术栈,和Gemini沟通几轮之后决定采用前后端分离的形式,前端负责UI、发牌动画、点击出牌、房间号输入框等;后端是游戏服务器(维护房间列表、洗牌发牌、校验出牌是否合法、计算分数,并通过 WebSocket 实时把画面变化广播给所有玩家)
因为我基本没有网站开发经验(也不需要有),技术栈全让AI决定了:
- 包管理:pnpm workspace
- 语言:TypeScript
- 前端:React 19、Vite 6、socket.io-client
- 后端:Node.js、Express、Socket.IO
- 构建:
- shared:
tsc - client:
tsc -b && vite build - server:
tsup(ESM 打包)
- shared:
开发过程
首轮开发
第一轮对话之后,codex捣鼓捣鼓发了个网站过来,没什么报错,是个好兆头,不过ui相当简陋

此外bug也不少,包括但不限于:ui非常丑,我希望的ui和微软游戏的界面类似,并且有选牌的动画,出牌特效等;此外我希望在打牌的进程中就可以看到四个人得了多少分,但实际上是一局结束后才显示;最后一局打完之后无法结束,也无法开下一局,应该定一个规则,就是分数最高的玩家达到多少分之后游戏结束,默认可以是100分;传牌阶段只能点快速传牌,不能自己选;机器人只能点击添加,添加之后好像不能修改难度…等等等等
优化UI
首当其冲的是改UI,这ui看着我就不想玩。上网找了找,搞了一套扑克牌的svg图给ai,但体积稍微有点大:不到8MB,这个是要加载到前端的素材,最好优化一下,不过我懒得搞了。
接着创建一个全新的 PlayingCard.tsx 组件,统一渲染所有卡牌;把背景改成绿的…反正大改ui,改完之后长这样:

偷懒用现在版本的图,不过刚改完也差不多了
是不是一下子就有感觉了?对比微软的版本:
这样看着就舒服多了!
但当时ui还有点小问题,比如中间的出牌区有时会和头像框重合之类的,反馈一下也好了
增加出牌动画(暂缓)
现在这个阶段已经是可玩的了,但所有的cpu出牌都是瞬间出,所以我出完牌之后看不到cpu出的牌这回合就结束了,有点难受,于是让agent增加一个慢速模式,不过改了两版之后,反而连开头的换牌都继续不了了,只能先回退
后面这个功能还得加。
跨域访问
这个项目是前后端分离的,用pnpm管理,pnpm --filter @hearts/client dev --host可以启动客户端 ,--host参数可以在局域网内都能访问,否则只有localhost可以用;pnpm --filter @hearts/server dev可以开后端,但这时加参数没用了,还是连不上后端,问ai是CORS的问题
修改了绑定的host为0.0.0.0 CORS 改为环境变量驱动process.env.CORS_ORIGIN || "*"差不多这样就行了
多设备适配
这下别的设备也可以访问了,但手机端没做适配,ui有问题,交给ai解决
后端打包成docker并部署
现在游戏基本可用了,下一步就是把后端打包成docker镜像,这样随便找台服务器放上去就行了
让agent写Dockerfile .dockerignore等配置文件,写好之后传到对应机器构建
不过遇到点问题:构建完之后无法运行,还没找到解决办法,暂时只能在本地跑后端了
前端部署到Vercel
前端比较好办,把项目传到github上之后从vercel拉下来就行,但由于这是一个monorepo,即客户端和服务端放在一个仓库里,还有shared文件夹存放了共享的代码,所以部署有些麻烦
首先根目录不能是packages/client,而是packages 因为client和shared都和前端有关
然后Build Command改为
1 | pnpm install && pnpm -F @hearts/shared build && pnpm -F @hearts/client build |
Output Directory改为client/dist
然后直接Deploy,前端就没啥毛病了
总结
AI编程还是很有趣的,整个项目我几乎完全没写过代码,让我写我也不会,但排查bug不断反馈还是相当累人的,而前端后端部署虽然ai也可以代劳,但研究之路还很长,就先这样吧
TODO
隐蔽的调试后门 (Hidden Debug Menu)
建议实现方式:在登录页或大厅的某个不起眼的角落(比如左下角的版本号文本),实现“连续点击 5 次”触发的隐藏彩蛋。
功能:弹出一个弹窗,允许手动输入后端的 WebSocket URL(例如
ws://192.168.x.x:3001),并将其持久化保存到浏览器的localStorage中。这样你在手机上测试时,随时可以切换后端地址,再也不用重新部署 Vercel。
前端管理员大盘 (Admin Dashboard)
入口:单独的路由(例如
/admin)。鉴权:最简单的实现方案是在前端输入一个密码,通过 WebSocket 发给后端校验(密码可以配置在后端的
.env中)。功能:获取并展示当前所有活跃房间的列表、房间内的玩家状态(真人/CPU)、当前进行到第几局、服务器内存占用等基本信息。
彻底攻克 Docker 打包 (Docker Build)
目标:解决 Node.js ESM 模块的路径解析报错(
ERR_MODULE_NOT_FOUND)。方案:在
packages/server引入tsup,将后端代码打包成单文件(Single Executable),彻底规避 Monorepo 目录结构和文件后缀带来的各种奇怪路径问题。
重构前端动作队列与出牌动画 (Action Queue)
目标:解决“AI 瞬间出牌导致玩家看不清”的问题。
方案:重新审视之前回退的代码。将 WebSocket 收到的事件先推入前端队列,按照固定的时间间隔(如 800ms)逐个播放,并配上 SVG 卡牌的平移飞行动画。
加入基础音效系统 (Audio Effects)
- 建议:寻找几个无版权的清脆音效:发牌声、出牌声、一墩结算收牌声、得分警告声(比如吃到黑桃 Q 时的特殊音效)。音效对棋牌类游戏的“打击感”提升是巨大的。
幽灵房间清理机制 (Garbage Collection)
目标:防止服务器长时间运行后内存泄漏。
方案:在后端的
RoomManager里加一个定时器。如果一个房间超过 2 小时没有任何操作,或者所有真人都断开连接超过 15 分钟,自动销毁该房间实例。
房主踢人与权限转移
- 功能:房主可以手动将一直不准备的玩家踢出房间;如果房主掉线,系统自动将房主权限移交给房间内的下一个真人玩家。