Data structures OCaml中的游戏数据结构
我目前正在制作一个类似计算机的物流系统(如minecraft mod applied energestics)的游戏/模拟 游戏的主要部分是二维方块网格 所有块都具有相同的属性,如位置 但是应该有不同类型的块,比如:Data structures OCaml中的游戏数据结构,data-structures,functional-programming,ocaml,2d-games,Data Structures,Functional Programming,Ocaml,2d Games,我目前正在制作一个类似计算机的物流系统(如minecraft mod applied energestics)的游戏/模拟 游戏的主要部分是二维方块网格 所有块都具有相同的属性,如位置 但是应该有不同类型的块,比如: 物品容器 输入和输出总线 等等 在命令式面向对象语言(如Java)中,我将通过以下方式实现这一点: 主块类 具有位置等公共属性 然后有子类 从block类继承的 这些子类将实现不同块类型的不同属性 在ocaml中,我有点不知所措 我可以创建继承的对象,但这与Java
- 物品容器
- 输入和输出总线
- 等等
- 主块类
- 具有位置等公共属性
- 然后有子类
- 从block类继承的
- 这些子类将实现不同块类型的不同属性
ocaml
中,我有点不知所措
我可以创建继承的对象,但这与Java中的工作方式不同
例如:
- 我不能将不同子类的对象放在一个列表中
type blockType = Container | Input | Output | Air
type block = {blockType :blockType; pos :int * int}
let rotateBlock block =
match block.blockType with
| Input -> {block with entity = nextDirection block.entity}
| Output -> {block with entity = nextDirection block.entity}
| _ -> block
我努力添加单个附加属性。我试图向块记录类型添加一个实体字段,该字段将保存其他属性:
type entity = Container of inventory | OutputEntity of facing | InputEntity of facing | NoEntity
(其中库存和饰面也是自定义类型)
这个解决方案感觉不太合适
我的一个问题是,我想对输入和输出类型的块执行一些逻辑操作。我必须重复这样的代码:
type blockType = Container | Input | Output | Air
type block = {blockType :blockType; pos :int * int}
let rotateBlock block =
match block.blockType with
| Input -> {block with entity = nextDirection block.entity}
| Output -> {block with entity = nextDirection block.entity}
| _ -> block
这对于两种类型来说并不是那么糟糕,但我计划添加更多,因此在可伸缩性方面这是一个很大的负面影响
这种结构的另一个关键点是它有点不一致。我使用记录中的一个字段在块级别实现不同的类型,在实体级别实现多个构造函数。我这样做是为了能够使用block.pos
轻松访问每个块的位置,而不是使用模式匹配
我对这个解决方案不是很满意
请求
我希望有人能为我指出关于数据结构的正确方向。听起来很有趣
我不能将不同子类的对象放在一个列表中
实际上你可以。假设有许多不同的块对象
所有人都有一个“衰变”的方法。你可以有一个功能“给我
它可以把所有的积木都列在一个清单上
然后,您可以按一定的时间间隔在列表上迭代并应用
每个块上的衰减方法。这些都是很好的类型和容易
与OCaml的对象系统有关。你不能做的是拿出一个
从这个列表中可以看出,事实上,这也是
一个气闸,我现在想把它当作一个成熟的气闸,
而不是腐烂的
每个类型只能有240个so变体。如果你打算再吃一点
块,一个获得额外空间的简单方法是
对你的区块进行分类,并使用例如固体岩石|液体熔岩
而不是岩石熔岩
type block = {blockType :blockType; pos :int * int}
一个区块在你的库存中的位置是什么?职位是什么
一个在世界上被开采出来的区块
现在有点像坐在地上,等着被人接走?为什么不呢?
保留正在使用的数组索引或映射键中的位置
表示世界上各个区块的位置?否则你也
必须考虑块对于同一个位置意味着什么,
或不可能的位置
let rotateBlock block =
match block.blockType with
| Input -> {block with entity = nextDirection block.entity}
| Output -> {block with entity = nextDirection block.entity}
| _ -> block
我并没有真正了解这个输入/输出的东西,但在这个
函数你对某种属性感兴趣,比如“有下一个方向”
如果旋转,则指向“面”。为什么不命名该属性并使其与您匹配
type block = {
id : blockType;
burnable : bool;
consumable : bool;
wearable : bodypart option; (* None - not wearable *)
hitpoints : int option; (* None - not destructible *)
oriented : direction option; (* None - doesn't have distinct faces *)
}
let rotateBlock block =
match block.oriented with
| None -> block
| Some dir -> {block with oriented = Some (nextDirection dir)}
let burn block =
match block.burnable, block.hitpoints with
| false, _ | true, None -> block
| true, Some hp when hp > 5 -> { block with hitpoints = Some (hp - 5) }
| true, Some hp -> ash
块类型很有趣,因为每种类型都有不同的操作 物品容器, 输入和输出总线, 等等 我的直觉告诉我,您也许可以使用GADT创建块上的操作类型,并为模拟器轻松实现一个计算器 更新 要回答您的评论: 如果您对所有变体都有一个共同的信息,您需要提取它们,您可以想象如下:
type block_info =
| Item of specific_item_type ....
| Bus of specific_bus_type
type block = {position:Vector.t ; information : block_info}
let get_block_position b = b.position
你在努力满足相互竞争的目标。不能同时拥有刚性静态块模型和动态可扩展块类型。所以你需要选择。幸运的是,OCaml为两者提供了解决方案,甚至为两者之间的问题提供了解决方案,但对于中间解决方案来说,它们在这两方面都有点糟糕。让我们试试看 使用ADT的刚性静态层次结构 我们可以使用sum类型来表示对象的静态层次结构。在这种情况下,我们很容易添加新方法,但很难添加新类型的对象。作为基本类型,我们将使用多态记录,该记录通过具体块类型参数化(具体块类型本身可以是多态的,这将允许我们构建层次结构的第三层等等) 其中,
info
是一个额外的特定于混凝土块的有效载荷,即,block\u info
类型的值。此解决方案允许我们编写接受不同块的多态函数,例如
let distance b1 b2 =
sqrt ((float (b1.x - b2.x))**2. + (float (b1.y - b2.y)) **2.)
distance
函数具有类型'a blk->'b blk->float
,并将计算任意类型的两个块之间的距离
此解决方案有几个缺点:
let distance b1 b2 =
sqrt ((float (b1.x - b2.x))**2. + (float (b1.y - b2.y)) **2.)
type block = Block : block_info -> {pos : pos; info : block_info}
type block_info = ..
type block_info += Air
class block x y = object
val x = x
val y = y
method x = x
method y = y
method with_x x = {< x = x >}
method with_y y = {< y = y >}
end
class input_block facing = object
inherit block
val facing = facing
method facing = facing
method with_facing f = {< facing = f >}
end
type block = int
type world = {
map : pos Int.Map.t;
facing : facing Int.Map.t;
air : Int.Set.t;
}
type block = {id : int; eq : int}
type block =
| Regular of regular
| ...
| Compose of compose_kind * block * block
type compose_kind = Horizontal | Vertical | Inplace