Haskell 生命游戏在启动时崩溃

Posted

技术标签:

【中文标题】Haskell 生命游戏在启动时崩溃【英文标题】:A Haskell Game of life crashes when launched 【发布时间】:2014-08-26 08:18:40 【问题描述】:

我目前正在尝试在 Haskell 中开发一个小型的康威生命游戏。我编写了一个库,lifegame,它可以管理单元格并计算其世代 (见github.com/qleguennec/lifegame.git)。 Generations 是一个无限列表。 到目前为止,该库运行良好,但缺少文档。这是一个小型图书馆,进入它应该不难。

现在,我来这里的目的是,我正在尝试使用 lifegamehelm 来在屏幕上实际显示世代。这是我写的:

import FRP.Helm
import FRP.Helm.Graphics (Element(..))
import qualified FRP.Helm.Window as Window
import FRP.Helm.Animation (Frame, AnimationStatus(..), animate, absolute)
import FRP.Helm.Time (second, running, delta)
import FRP.Elerea.Simple

import Cell.Display (allGenFrames)
import LifeGame.Data.CellGrid (CellGrid(..), randCellGrid)

render :: Form -> (Int, Int) -> Element
render form (x, y) = collage x y $ [form]

main :: IO ()
main = do
  cg <- randCellGrid 50 50
  anim <- return . absolute $ allGenFrames cg (1 * second) 10
  engine <- startup defaultConfig

  run engine $ render <~ (animate anim running status) ~~ Window.dimensions engine

  where
    config = defaultConfig  windowTitle = "bats"
                           , windowDimensions = (500, 500)
    status = effectful $ getLine >>= \i -> return $
      case i of
        "Pause" -> Pause
        "Stop" -> Stop
        "Cycle" -> Cycle

(来自:github.com/qleguennec/bats.git)

计算的艰苦工作在animate running status",第20行。我不太明白animate的第二个参数是什么。此外,我不确定提供无限的Frames列表是否合法。

当我启动代码时,游戏冻结并在 5 分钟后停止。它似乎消耗了所有内存并崩溃。现在,我知道所有这些都缺乏文档。我在做这个工作。但是,作为婴儿 Haskeller 和婴儿 FRP/SDL 开发人员,我需要知道我是否以错误的方式执行此操作(我可能会这样做)。 接受并推荐任何评论。谢谢。

【问题讨论】:

【参考方案1】:

我不知道我能否在 FRP 方面为您提供帮助 - 有这么多库,我自己也没怎么玩过 - 但它的价值(因为你说你对 Haskell 很陌生), Conway's Life 不用 FRP 也能轻松写出,用 SDL 渲染也很简单。

我发现最好将游戏表示为一组坐标对,而不是一个数组 - 这样,您就没有边界条件或世界环绕问题。 (您可以从 hashmapunordered-containers 包中获得高效的严格集或惰性集。

CA 逻辑如下所示:

next cs = [i | (i,n) <- M.toList neighbors,
           n == 3 || (n == 2 && S.member i cs')]
 where
  cs'         = S.fromList cs
  moore (x,y) = tail $ liftM2 (,) [x, x+1, x-1] [y, y+1, y-1]
  neighbors   = M.fromListWith (+) $ map (,1) $ moore =<< cs

主程序循环如下所示:

run w cs = do
  drawCells w cs
  e <- pollEvent
  case e of
   KeyUp (Keysym SDLK_ESCAPE _ _) -> return ()
   KeyUp (Keysym SDLK_SPACE  _ _) -> pause w cs
   _                              -> run w $ next cs

drawCells w cs = do
  fillRect w (Just $ Rect 0 0 xres yres) (Pixel 0)
  c <- createRGBSurface [SWSurface] cellSz cellSz 32 0 0 0 0
  mapM_ (draw c . scale) cs
  SDL.flip w
 where
  rect (x,y) = Just $ Rect x y cellSz cellSz
  scale      = join (***) (* cellSz)
  draw c p   = do fillRect c Nothing $ Pixel 0xFFFFFF
                  blitSurface c Nothing w $ rect p

要查看完整的实现,以及 FRP 可以为您提供的大部分功能(通过鼠标编辑网格)等等,take a look at this.

【讨论】:

以上是关于Haskell 生命游戏在启动时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Haskell中的懒惰和尾递归,为啥会崩溃?

如何在 Haskell 中编写游戏循环?

如何在纯 Haskell 中编写简单的实时游戏循环?

将抽象数据类型保存和加载到 json 文件并从游戏中的文件中读取 Haskell

用 90 行 Haskell 代码实现 2048 游戏

90行代码写一个游戏?教你用90行HasKell代码实现2048游戏