Liyi Zhang
XXX

Poker Game

一个Web扑克游戏 —— 用不着机器学习也能解决这个问题

A poker game based on browser, only using basic algorithms

游戏地址

游戏地址

概要

这里我主要用中文来讲述poker game的完成细节,一些注意点和坑,以及单人web项目开发过程中的体会

I'll mainly express in Chinese, including details, not-to-dos and how should I develop small projects.

技术栈

  • Javascript (ES6, Lodash, WebWorker)
  • React
  • GatsbyJS

起因

偶然一个论坛的帖子,讨论扑克牌残局。事实上我以前也经常看这类帖子,包括下面引发的一众讨论,但其实很多残局用穷举的方法就能知道获胜方法,让某些讨论完全可以用程序来替代。

I've read some posts on BBS discussing poker matches, in fact, some are not quite meaningful because a simple program could do it all.

思路

事实上这个思路很简单,用不着多高深的算法,首先枚举所有对局情况,生成决策树。再把结果从底至上保存

The idea is to enumerate all the possible choices and generate a decision tree. Then save the results from bottom to top.

实现过程

  • 写一个决策树生成算法,输入是开始的对局情况和先后手,其中还包括计算所有可能出牌方式的算法,这里使用了loadsh库来简化部分计算

    An algorithm to get a decision tree, params are cards and isFirst flag

getDecisionTree(cardsA, cardsB, isAfirst)
  • 在Gatsby中引入这个算法,并组件化构建相关的页面

    import this algorithm in gatsby and build related components

  • 构建的过程中发现,如果完全使用React Hooks来写逻辑,会变得非常复杂。因为对于游戏而言,组件间的交互是非常重的,所以最后删除了几乎所有Component的Hook,使用一个全局变量来保存所有游戏数据,这样也方便使用本地存储进行自动保存

    I discovered that there will be heavy interaction between components while using hooks, so I use a "global variable" instead.

  • 通过一个状态机(DFA)使用该全局变量,当然也可以用Redux的概念去理解,即每个处理action的函数, 接受一个action和相关的参数,找到当前的游戏状态,然后去执行相应的操作,返回新的状态

    This global variable works with a DFA,or you can understand it using concepts from Redux, like there is a function which receives an action and some parameters, and then, get the new state from previous state.

  • 最后定义一系列的游戏状态,和相关的参数,我发现确实可以用一个比较复杂的状态机来描述整个游戏过程。唯一美中不足的是,游戏需要暂停,也就是说停止响应任何除了接触暂停操作之外的动作,那么这时候需要写一个额外的逻辑。

    Finally, I defined several game states and found my DFA did perfectly implement the game except for some extra logic handling the pause-resume action.

//definition
const act = (prevState, action, params, forced)=>{
    if (prevState.paused && action!=ACTION.RESUME) return prevState
    ...
    return newState
}

//usage
state = act(state, ACTION.DO_SOME_THING, [1,2], false)
  • 对于游戏来说UI很重要,但我直接凭多年看卢姥爷直播录像的经验画了,吐槽一下,用CSS画界面真的非常复杂而且耗时,因为一开始想的是组件式的开发,后来就没改,如果重来的话我会用canvas

    UI is important for a game, but I just draw it according to my personal experience. It was an exhausting work and if I can redo it, I will use canvas.

  • 开发的过程中我发现,使用一整个DFA来定义游戏状态真的是非常方便的做法,方便增删功能,也几乎不用与展示层做任何交互

    It's really convenient to use a whole DFA to define game state if you want to add or delete some features without changing components for present.

  • 最后,我使用了workerize-loader来实现异步计算的功能,毕竟JS是单线程的,在WebWorker技术出现之前这种重计算的场景简直是天方异谈,重则浏览器奔溃,轻则网站卡死,感谢WebWorker!

    At last, I used workerize-loader to add support for asynchronized computing, thanks for webworker!

单人项目开发的经验和思考(Chinese Only)

首先从一个想法到想法的完全实现用了接近一个月的时间,当然这里的实际编码时间只有10天左右(估计每天5小时左右)。主要的瓶颈在于首先这是个临时起意的想法,也就是我之前从来没有接触过任何相关或类似的项目,那么就很有可能出现进行到一半需要重构整个项目的情况,事实上的确,当我发现用另一种办法(使用全局游戏变量)可以加快开发时,我果断重构了整个项目。次要的瓶颈在于UI,事实上,用CSS画UI的功夫足够我写十个算法了,还是使用canvas比较自由和方便。

解决了这两个问题,我相信项目的进度会大大加快,因为纯粹的技术问题通过stackoverflow或者google,几乎不会超过10分钟就可以得到解决。

所以,今后的web个人项目,我会做的是:

  • 判断是否有已知的类似项目,直接找到自己做过或者他人做过的类似项目,在此基础上进行修改,而并不是自己从头开始,即使是有相关的框架
  • 除了UI以外的算法层面自己实现,UI使用基于某种游戏框架的库,加快开发时间,减少在适配和美工等方面浪费的时间

另外,考虑到Chrome即将取消对Flash的支持,某些轻量级的游戏,实际上就失去了生存的空间,使用一些基于Html5的游戏框架(甚至不需要复杂的引擎,只需要UI库的支持),加上PWA,有没有可能快速完成一些好玩有趣又轻松的游戏呢?

可能需要完善的地方

  • 在需要的地方加上帮助
  • 提供一些默认的经典残局
  • 把默认的alert换成其他更友好的提示
  • 其他的欢迎来githubissue