功能性香蕉旅行者 - 船舶系统

时间:2015-08-06 14:23:03

标签: haskell types

在这个游戏中,我希望拥有玩家级别,玩家级别,以及根据级别和级别可能拥有的船只。您一次只能拥有一艘船。 我得出结论,我需要的是datakinds和数据系列。

这是我所拥有的片段,然后是我的问题。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses}
{-# LANGUAGE ConstraintKinds #-}

data PlayerClass = Pirate | Merchant | SpaceNinja deriving Show
data ShipClass = Tiny | Medium | Large deriving Show
data Merchant = Rookie | Swindler | IndustryCaptain deriving Show
data Pirate   = Lubber | Matey | Captain deriving Show

data family PShip (a :: PlayerClass) (b :: ShipClass)
data instance PShip Pirate Tiny = Junk
  { _hull_junk              :: Hull
  , _bridge_junk            :: Bridge
  , _cargo_compartment_junk :: CargoCompartment
  , _fuel_tank_junk         :: Tank
} deriving Show

我的理解是,通过DataKinds,我可以将条款推广到类型,但仍然将它们用作条款。我想要一个类来抽象PShips的操作实现。我想约束类型级别(PlayerClass),然后为类型的类型创建实例。这是我想要做的事情的片段,但不能。

 class (PShip a) => Ship a (b :: PlayerClass) where
  ...
 instance Ship (Pirate Tiny) Lubber ...

这有意义吗?我是在正确的轨道上,还是应该备份并重新考虑这个问题?我清楚地阐明了这个问题吗?

1 个答案:

答案 0 :(得分:2)

不要在类型级别强制执行“根据类和级别可能拥有的船只”的约束。它可能是OP,但是(可能)游戏不应该与1级空间忍者一起工作,这是一艘19世纪的大型船只。游戏规则可以写成普通函数,甚至可以编码为数据,而不是诉诸类型级编程。

如果您想从他们的班级和船舶尺寸中选择某人正在使用哪艘船,您只需编写一个功能。

data Ship = Ship {
    _hull_junk              :: Hull,
    _bridge_junk            :: Bridge,
    _cargo_compartment_junk :: CargoCompartment,
    _fuel_tank_junk         :: Tank
}

data Hull = Raft | Sloop | Brig | Schooner | Starfighter | ...

playerShip :: PlayerClass -> ShipClass -> Ship
playerShip Pirate Tiny = Ship {
    _hull_junk = Sloop,
    _bridge_junk = ...
}
playerShip SpaceNinja Tiny = Ship {
    _hull_junk = Starfighter,
    _bridge_junk = ...
}
...

我们已经将关于哪艘船与玩家和船只类的规则从类型级别移动到普通Haskell代码级别的规则。

您指定了按类和级别划分的多艘船只。为了处理这个问题,我们将根据玩家的等级和船舶尺寸返回船只列表

allowedShips :: PlayerClass -> ShipClass -> [Ship]

为了轻松实现这一点,我们可能会制作一个游戏数据文件,其中包含船只列表,并为每艘船舶列出可用于哪些玩家类别和船舶类别的列表。通过这样做,我们将关于哪些船只可以通过玩家和船舶类从普通Haskell代码级别移动到数据级别的规则。

[
    ([(Pirate, Tiny)], Ship {
        _hull_junk = Sloop,
        _bridge_junk = ...
    }),
    ([(SpaceNinja, Tiny)], Ship {
        _hull_junk = Sloop,
        _bridge_junk = ...
    }),
    ([(Pirate, Tiny),(Merchant, Tiny),(SpaceNinja,Tiny)], Ship {
        _hull_junk = Raft,
        _bridge_junk = ...
    }),
    ...
]

首先,您可以在.hs文件shipData :: [([(PirateClass, ShipClass)],Ship); shipData = ...中列出此类数据,并通过盗用海盗类和发货类过滤allowedShips来实施shipData

如果所有_junk都是数据,我们可以更进一步将此数据视为数据,将其存储在文件中并使用派生的Read实例进行读取。