为了学习Haskell,我决定制作一个Minesweeper克隆。我已经编写了一些代码,程序的主要结构已经准备就绪。它基于:
A Board
,包含amount
地雷的,地图fields
的width
以及height
和的列表。
一个Field
,包含相邻地雷的数量或一个指示字段本身是地雷的值。该Field
还含有FieldState
(Marked
,Hidden
或Shown
)。
游戏循环如下所示:
play board = do -- Clear the screen putStrLn $ replicate 40 '\n' -- Show the board print board -- Get and process user input putStr "Command: " -- We are generating a new board to reflect the changes! newBoard <- parseInput board <$> getLine -- If the game is not ended, go to next step if gameEnded newBoard then return $ result newBoard else play newBoard
目前,这种方法效果很好,但我不喜欢每次转动后都必须产生一个新的事实Board
。从C ++这样的命令性语言来看,它看起来效率很低,您只需要对现有的电路板进行改动即可。
要点:
这是正确的方法吗?
编译器会优化它吗?
leftaroundab.. 5
当然,完全更换电路板并非最佳选择,但您的想法也不一定像您想象的那样糟糕。如果Board
使用[[FieldState]]
-2D网格,则更新单个单元格实际上会重用大多数行(这种工作无需考虑,并且非常安全,因为 Haskell纯粹是起作用的):对于n × n网格,复杂度为O(n),仅比您查找任何给定字段所需的常数差。对于Minesweeper这样的游戏,绝对不会有任何问题。
对于性能更关键的事情,有一些专用的数组类型可以同时进行O(1)中单个字段(在IO
or或ST
monad中)的查找和更新。对于与您的应用程序类似的应用程序来说,它可能是最合适的(尽管社区中存在某种趋势,宁愿围绕更低级别,更优化的向量类型编写您的抽象)。Data.Array.MArray
当然,完全更换电路板并非最佳选择,但您的想法也不一定像您想象的那样糟糕。如果Board
使用[[FieldState]]
-2D网格,则更新单个单元格实际上会重用大多数行(这种工作无需考虑,并且非常安全,因为 Haskell纯粹是起作用的):对于n × n网格,复杂度为O(n),仅比您查找任何给定字段所需的常数差。对于Minesweeper这样的游戏,绝对不会有任何问题。
对于性能更关键的事情,有一些专用的数组类型可以同时进行O(1)中单个字段(在IO
or或ST
monad中)的查找和更新。对于与您的应用程序类似的应用程序来说,它可能是最合适的(尽管社区中存在某种趋势,宁愿围绕更低级别,更优化的向量类型编写您的抽象)。Data.Array.MArray