首页 > 解决方案 > 使用方向移动的文字冒险游戏问题

问题描述

我在 Haskell 的地图上移动时遇到问题。如果玩家的输入是 eg BackOff,我需要将当前位置更改为新位置,但我还需要检查是否有位置。

我不确定如何编写move函数。我还想向玩家展示他们可以从当前位置移动到的位置(一次只能移动一个空间。)

我的第一个想法是检查输入是否是TurnLeft例如,然后减少Direction以使其达到West(我假设我将其编程错误)并增加以使其达到East等等。

如何检查玩家想要旅行的位置是否可用,然后如何更改位置?

location = "Fort Neugrad" -- the player starts in Fort Neugrad first

currentDirection = North -- the player faces north at first

data Direction = North | East | South | West deriving(Show, Eq, Enum)

data Movement = TurnLeft | TurnRight | Advance | BackOff deriving(Show)

type Location = String

type GameMap = [((Location, Direction), Location)]   -- First location is the     current location, second is the next location where the direction points to(Ive made a map but its to large to post it)

--move :: Movement -> Direction -> Location
--move m = case m of TurnLeft -> pred currentDirection
       --            TurnRight -> succ currentDirection
       --            Advance ->
       --            BackOff ->


filterAccess :: Location -> [Location]
filterAccess l = [ c | ((a,b), c) <- wonderland, a == l ]

标签: haskell

解决方案


我在你的设计中看到的第一件事是你有全局常量locationcurrentDirection. 这与 Haskell 的工作方式不兼容:因为 Haskell 是纯函数式的,你永远无法更改locationcurrentDirection. 相反,你需要类似的东西

data Player = Player { location :: Location
                     , direction :: Direction }
            deriving (Eq, Ord, Show, Read)

然后你会想要move类型

move :: Player -> Movement -> Player

要注意的第二件事是你给出move了 type Movement -> Direction -> Location,但实际上你并没有给它一个Movement和一个Direction参数——你只给了它一个参数,the Movement。相反,我们想要类似的东西

move :: Player -> Movement -> Player
move p m = case m of {- ... -}

请注意代码中没有的额外p参数。

现在,我喜欢使用predsucc用于转动的概念!不幸的是,问题在于,如果它们溢出,而不是环绕predsucc会引发错误,所以我们需要自己编写。

turnRight :: Direction -> Direction
turnRight North = East
turnRight East  = South
turnRight South = West
turnRight West  = North

turnLeft :: Direction -> Direction
turnLeft North = West
turnLeft East  = North
turnLeft South = East
turnLeft West  = South

在这里,我使用了“等式风格”,而不是turnLeft d = case d of .... 它更常见,但两者是等价的。

所以,在 中move,我们有

move :: Player -> Movement -> Player
move p m = case m of
  TurnLeft  -> p{direction = turnLeft  $ direction p}
  TurnRight -> p{direction = turnRight $ direction p}
  Advance   -> undefined
  BackOff   -> undefined

(在这里,我切换回“表情风格”,用case. 再次,同样的事情!)

现在,为了前进和后退,我假设您正在使用wonderland类型为GameMapand的值filterAccessGameMap有点可疑:如果你想要它,最好使用普通的三元组(Location, Direction, Location)而不是嵌套对。现在,filterAccess它有点狡猾,因为它只检查起始位置而忽略方向。但更好的设计可能是使用 real Map,它可以让您直接按键查找。确保你添加Ord到你的派生类中Direction,然后你就可以拥有

import Data.Map (Map)
import qualified Data.Map as M

type GameMap = Map Location (Map Direction Location)
  -- Or `Map (Location, Direction) Location`

nextLocation :: Player -> GameMap -> Maybe Direction
nextLocation Player{location = loc, direction = dir} gmap =
  case M.lookup loc gmap of
    Nothing -> Nothing
    Just m' -> M.lookup dir m'
-- Or:
--     nextLocation Player{location = loc, direction = dir} gmap =
--       M.lookup dir =<< M.lookup loc gmap

这应该填补进展中的情况。后退情况类似,您只需要填写“向后方向”逻辑:-)


推荐阅读