2KB项目,专业的源码交易网站 帮助 收藏 每日签到

用 90 行 Haskell 代码实现 2048 游戏

  • 时间:2019-01-23 18:35 编辑:2KB 来源:2KB.COM 阅读:292
  • 扫一扫,手机访问
  • 分享
摘要:
Haskell 英文原文:Implementing the game 2048 in less than 90 lines of Haskell

上个星期赖斯大学的MOOC 计算的规则 公开课在 Coursera 上开讲啦. 从第一周的材料来看,看起来它有了他们之前的课程 Python中的交互式编程介绍 所有优良的东西: 演示文稿做的很不错,也有大量的支持可用, 而布置的作业也很有趣. 第一个作业就是编写 2048 游戏的逻辑.

鉴于其设计中的根本性缺陷,我并不认为2048特别的有趣. 首先,你并不能在某个地方取得游戏的胜利. 其次,最有希望的游戏策略使得其玩起来相当的繁琐,而且最大的乐趣并不是自己的游戏技能而是随机数生成器制造的幸运连胜. 就我个人而言,更愿意选择那种有时被称为“理论完美”的游戏, 比如,游戏的一个属性使得玩它的人能够取得一个确定的胜利. 而2048的游戏结果却没有吸引到我,不过我也明白为什么会有人喜欢让瓷砖四处滑动起来.

为游戏的逻辑编写代码是相当直接的。归因于使用Python作为教学语言的计算原则课程, 对于在我的最初版本中的一个错误是由于python发生了改变,我不会感到奇怪. 我想着用Haskell写这个东西可能会更有趣, 随后就着手开始用这个语言编写了2048的一个完整实现, 包括 I/O 处理. 整个代码可以在 我的git账号 上找到. 最终结果证明,更加完整的Haskell方案所需要的代码比使用Python的程序逻辑要少几行.

作为说明,如果你到这个页面来只是为了找寻计算规则这门课程的Python作业的解决方案,那你就是在浪费时间. Haskell的实现和Python的实现很不同,使用的编程语言构造也不能在Python上用. 换言之,如果你正纠结这个作业,Haskell的源代码将不会对你有所帮助.

在这篇文章中,我仅想着重强调游戏逻辑的核心部分,因为它很好地显示了函数式编程的力量。首先,我定义一个数据类型,用于展示网格中的数字移动的方向,还有一个用于存放整数列表的列表的类型同义词,用来提高类型特征的可读性。从函数‘move’的命名可以明显看出函数的作用;再下一步,将输入作为一个网格的数字和移动方向,并产生新的网格。

data Move = Up | Down | Left | Right
type Grid = [[Int]]

2048这个游戏是在一个4x4的棋盘上进行的。开始位置在我的实现中是固定的:

start :: Grid
start = [[0, 0, 0, 0],
         [0, 0, 0, 0],
         [0, 0, 0, 2],
         [0, 0, 0, 2]]

棋盘上可以在4个方向上对数字进行移动,意味着所有的数字的移动都会向着一个指定的方向,如果是2个数字,移动相同的方向,以彼此相临而告终,则他们合并到一起。举例来说,在如下所示的起始位置,移动方向为‘Up’,结果棋盘变成了下面所示:

[[0, 0, 0, 4],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0]]

如果网格中的起始位置移动方向为向右,则不会有任何变化。如果网格变化了,则一个新的数字会在任何空的格子中产生,这个数字可能是2或者4.

我们看这种方法,问题在于其如何更有效的建模。在网格中的任何行列,都可被理解为一个列表。行和列表之间的关系是简单明了的。列将不得不提取、 修改,或虽然再,插入。或者他们不需要?

我写了一个函数来合并一行或一列,表示为一个列表。首先,所有的0要被移动,然后该列表将被处理,合并相邻元素,如果它们包含相同的数字,接着如果必要的话,为结果中填充0.

merge :: [Int] -> [Int]
merge xs = merged ++ padding
    where padding          = replicate (length xs - length merged) 0
          merged           = combine $ filter (/= 0) xs
          combine (x:y:xs) | x == y    = x * 2 : combine xs
                           | otherwise = x     : combine (y:xs) 
          combine x        = x

当棋盘中的移动方心为左时,这个合并函数可以立刻被应用。其他方向的移动,然而,需要进行一些考虑,如果希望代码保持简洁。向右移动网格是通过采取反转它之前将它提交给函数merge的每一行完成的,然后再次反转结果:

move :: Grid -> Move -> Grid
move grid Left  = map merge grid
move grid Right = map (reverse . merge . reverse) grid
move grid Up    = transpose $ move (transpose grid) Left
move grid Down  = transpose $ move (transpose grid) Right

对于网格向上或者向下移动,如果你想提取出一列,对其应用合并函数,然后产生新的网格进行列的插入,这是极其痛苦的。相反,虽然一点点的线性代数知识,却导致一个更优雅的解决方案。如果你不能立即明确如何移调导致所期望的结果,请看看下面的插图。

        input       transpose   move        transpose

Up:     0 0         0 2         2 0         2 2
        2 2         0 2         2 0         0 0


Down:   2 2         2 0         0 2         0 0 
        0 0         2 0         0 2         2 2

我Haskell的实现使用终端作为输出。它不像Gabriele Cirulli版本的JavaScript前端一样令人印象深刻,但它是可维护的,如下两个屏幕截图展示:

Starting position

Game Over

总体来讲,我对于这个原型还是很满意的。当然有几个可能的改进。一个分数跟踪器的添加将是微不足道的,虽然一个 GUI 将是一个更加耗时的努力。如果有立即响应键盘输入的程序,我会觉得这个很有趣。当前,每个通过 WASD的输入 需要点击回车键进行确认。如果只按一个键将触发程序执行的下一步,那么游戏玩法会加快很多。在研究这一问题时,我没有找到任何快速的解决办法。尽管Haskell库NCurses包含键盘事件。我可能会深入探究一下,如果我用ASCII 图形进行编程使之成为一个“独立”游戏。

如果你觉得这篇文章有趣,请随意看看我的 2048的 Haskell 实现的源代码

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部