如何在Haskell中为二维世界建模

如何在Haskell中为二维世界建模,haskell,Haskell,我在做游戏。游戏由一个无限平面组成。单位必须在一个离散的正方形上,因此它们可以用一个简单的位置{x::Int,y::Int} 可能有许多种单元s。有些可能是生物,有些只是物体,比如一块石头或木头(想想那里的2d地雷船)。很多都是空的(只是草或别的什么) 你将如何在Haskell中对此进行建模?我已经考虑过做下面的事情,但是关于物体对生物呢?他们可能有不同的领域?把他们都正常化 data Unit = Unit { x :: Int, y :: Int, type :: String, ... m

我在做游戏。游戏由一个无限平面组成。单位必须在一个离散的正方形上,因此它们可以用一个简单的
位置{x::Int,y::Int}

可能有许多种
单元
s。有些可能是生物,有些只是物体,比如一块石头或木头(想想那里的2d地雷船)。很多都是空的(只是草或别的什么)

你将如何在Haskell中对此进行建模?我已经考虑过做下面的事情,但是关于物体对生物呢?他们可能有不同的领域?把他们都正常化

data Unit = Unit { x :: Int, y :: Int, type :: String, ... many shared properties... }
我也考虑过使用位置类型

data Location = Location { x :: Int, y :: Int, unit :: Unit } 
-- or this
data Location = Location { x :: Int, y :: Int }
data Unit = Unit { unitFields... , location :: Location }
你有什么想法吗?在OO语言中,我可能会让
Location
Unit
从另一个继承,并使特定类型的单元相互继承


另一个需要考虑的问题是,这将通过网络发送大量这些对象,因此我需要将它们序列化为JSON,以便在客户端上使用,并且不想编写大量的解析样板文件

位置
只是一个简单的二维
类型

我建议不要将
单元
绑定到它们所在的位置;只需使用
地图定位单元
来处理网格上各个位置之间的地图以及存在的内容(如果有的话)

至于
单元的具体类型,我至少建议将公共字段分解为数据类型:

data UnitInfo = UnitInfo { ... }

data BlockType = Grass | Wood | ...

data Unit
  = NPC UnitInfo AgentID
  | Player UnitInfo PlayerID
  | Block UnitInfo BlockType
或类似的

通常情况下,将常见的事物分解为它们自己的数据类型,并尽可能保持数据的简单和“隔离”(即,将诸如“此单元位于何处?”之类的内容移动到将两者关联起来的单独结构中,以便各个数据类型尽可能“永恒”、可重用和抽象)

在Haskell中,
单元的“类型”具有
字符串
,这是一种强烈的反模式;它通常表示您试图用数据类型实现动态类型或OOP结构,这是不合适的

您的JSON需求使事情变得复杂,但它展示了一个很好的示例,说明了如何在Haskell中习惯性地实现这种泛型,而不使用
String
typing或奇特的类型类hacks,使用函数和数据类型作为主要抽象单元。当然,前者会给你带来问题;很难将函数序列化为JSON。但是,您可以维护来自ADT的地图,该地图表示“类型”生物或块体等,以及其实际实现:

-- records containing functions to describe arbitrary behaviour; see FAQ entry
data BlockOps = BlockOps { ... }
data CreatureOps = CreatureOps { ... }
data Block = Block { ... }
data Creature = Creature { ... }
data Unit = BlockUnit Block | CreatureUnit Creature
newtype GameField = GameField (Map Point Unit)

-- these types are sent over the network, and mapped *back* to the "rich" but
-- non-transferable structures describing their behaviour; of course, this means
-- that BlockOps and CreatureOps must contain a BlockType/CreatureType to map
-- them back to this representation
data BlockType = Grass | Wood | ...
data CreatureType = ...
blockTypes :: Map BlockType BlockOps
creatureTypes :: Map CreatureType CreatureOps
这使您拥有典型OOP结构的所有可扩展性和不重复的特性,同时保持功能的简单性并允许简单的游戏状态网络传输


通常,您应该避免考虑继承和其他OOP概念;相反,试着从功能和简单结构的组成方面考虑动态行为。函数是函数编程中最强大的工具,因此得名,它可以表示任何复杂的行为模式。最好不要让网络游戏等需求影响您的基本设计;像上面的例子一样,几乎总是可以将这些东西放在为表达性和简单性而构建的设计之上,而不是像通信格式这样的约束。

使用
Data.Map.Map
作为网格。那么x和y是否只是地图中的一个键,而单位只是有其不同的字段?当需要将它们发送到客户端时,我是否也应该在那里发送一个映射/散列呢?仍在消化,但非常有用。非常感谢。有一件事让我感到困惑,那就是id字段。我计划在redis中存储状态,JSON api将需要一些ID方面的通信。我应该向我的单位添加id字段,还是以某种方式将其关联?例如,一个新客户可能想说“我是新的!把我的玩家对象还给我!”他们想要自己的id,但他们会发送一些字段来指定他们的玩家。服务器生成并返回它。所以他们的对象没有,我返回的对象有。@SeanClarkHess:是的,一个
IntMap单元
或某种将标识符与单元关联的东西可能是最好的选择;在
单元
中包含标识符本身也是合理的。我会考虑将客户端指定的字段分解为它们自己的结构,并将其包含在<代码>生物< /代码>中,这样客户端可以决定的“无害”比特与它们不能接触的东西隔离(例如健康点);说
createuretemplate
@SeanClarkHess:然后你只需要例如
makebiote::createuretemplate->biote
createcreateureunit::biote->M Unit
(对于某些单子
M
)。